Files
Naomi Kramer b923c39e2d Update configuration (#78)
* Update on config structure, functionality, and tests

Co-Authored-By: Naomi Kramer <naomiagoddard@gmail.com>

* Extend subnet type to read/write from db, update tests

Co-Authored-By: Liza Tsibur <liza@activecountermeasures.com>

* updated read file config test and subnet tests

* fixed config and util tests, updated subnet related functions

Co-Authored-By: Naomi Kramer <naomiagoddard@gmail.com>

* Remove error return from GetDefaultConfig

Co-Authored-By: Liza Tsibur <liza@activecountermeasures.com>

* added json tags to database struct

* Updated beacon weights validation for config

* updates to score thresholds validation tags

* changes to config subnet validation and testing

* Update subnet.go

* Write missing host entries to http to populate http_proto

* Updating some fields to uint64

* WIP update some field types

* Update zeek count types and fix tests

* Add clickhouse credentials

* Misc fixes

* Update pointer

* Add ability to mark datasets as sample datasets

* fix column name

* Fix datasets exiting import if hour is empty

* Fix zeek count parsing from TSV files

* Remove storing dns conns in arrays, Fix historical first seen dns lag

* Remove unused columns

* Update config.hjson

* Update config.hjson

* updated impact category score functions to use float64

Co-Authored-By: Naomi Kramer <naomiagoddard@gmail.com>

* Update subnet.go

* Store import version in imports table

* Fix duplicated SNI/IP long connections

* Update subnet_test.go

* Cleanup output

* Rolling files updates (#39)

* Limit number of days to import for rolling datasets

* Fix breaking imports when import was interrupted

* Remove debug output

---------

Co-authored-by: Naomi Kramer <naomi@activecountermeasures.com>

* Omit parts of env from output

* Set max for threat intel datasize

* Remove SELinux neutering for QA

* Add network size column

* Fix http_proto for missing host, update tests for missing host fixes

* Add online feeds to default config

* Update sshprep (#45)

* Update sshprep

Co-Authored-By: William Stearns <3538265+william-stearns@users.noreply.github.com>

* Update sshprep

Add Bradley's suggestion of using head -1 to limit to a single address.

---------

Co-authored-by: Naomi Kramer <naomi@activecountermeasures.com>
Co-authored-by: William Stearns <3538265+william-stearns@users.noreply.github.com>

* Installer Behavior Tweaks (#41)

* Add --yes flag to add-apt-repository command

* Add missing sudo flags, make sure we're using the SUDO variable instead

* Add ability to perform zone transfers (#48)

* Store zone transfer records

Co-Authored-By: moth <25512187+0x6d6f7468@users.noreply.github.com>

* Update config

* Add tests

* Tests, connectivity test

* Update tests

---------

Co-authored-by: moth <25512187+0x6d6f7468@users.noreply.github.com>

* Support RedHat/RHEL as a valid target (#47)

* Update sshprep

Co-Authored-By: William Stearns <3538265+william-stearns@users.noreply.github.com>

* Supporrt RedHat/RHEL as a valid target

---------

Co-authored-by: Naomi Kramer <naomi@activecountermeasures.com>
Co-authored-by: William Stearns <3538265+william-stearns@users.noreply.github.com>
Co-authored-by: moth <moth@blackhillsinfosec.com>

* Fix tests (#49)

* Fix tests

* Update WalkFiles to use UTC

* fixed issue with rolling datasets over 24hours old not getting historical first seen timestamp set (#52)

* Change values from float32 to float64 (#50)

* Switch float32 to float64

* Update threat category calculation to match CalculateBucketedScore (#51)

---------

Co-authored-by: Liza Tsibur <liza@activecountermeasures.com>

* Bump max query execution time default value

* Use string instead of error for ZoneTransferConnectivityErrors struct fields (#61)

* Upgrade Golang to version 1.24 (#59) (#60)

* Replace get_url with shell and curl (#58)

* Update sshprep

Co-Authored-By: William Stearns <3538265+william-stearns@users.noreply.github.com>

* Replace get_url with shell and curl

* Use get_url by default, fall back to curl if it fails

---------

Co-authored-by: Naomi Kramer <naomi@activecountermeasures.com>
Co-authored-by: William Stearns <3538265+william-stearns@users.noreply.github.com>
Co-authored-by: moth <moth@blackhillsinfosec.com>

* add automated log transfer, AC-Hunter issue 135 (#62)

* Update sshprep

Co-Authored-By: William Stearns <3538265+william-stearns@users.noreply.github.com>

* add automated log transfer, PR135

* cron requires non-executable permission

* Specify suggested YAML plugin and config in VSCode workspace

* Linting and light cleanup

* Update generate_installer.sh

Download zeek_log_transport.sh to send to the sensor.

* Create cron file if remote zeek installation

* Only run zeek log import steps for remote sensor installations

---------

Co-authored-by: Naomi Kramer <naomi@activecountermeasures.com>
Co-authored-by: William Stearns <3538265+william-stearns@users.noreply.github.com>
Co-authored-by: moth <moth@blackhillsinfosec.com>

* Temporarily disable RITA/Zeek log transport until installer is modular (#66)

* Uniform -y flag usage for repo management/package installation; Uniform SUDO variable usage (#68)

* Resolve Installer Side Effects and Formalize RHEL Support (#73)

* Add missing necessary wildcards for RHEL versions

* Remove Ansible task replacing python3-requests to avoid RHEL distro installation side effects

* Update supported distros in README

* Update scoring defaults

* Resolve Ansible Reboot Errors (#75)

* Clean up conditionals; Fix reboot step for Ubuntu

* Suppress erroneous error output on RPM systems, ignore errors on reboot necessity checks

* Ignore missing host rows for openhttp (#76)

* Fix integration tests due to prevalence (#77)

---------

Co-authored-by: Liza Tsibur <liza@activecountermeasures.com>
Co-authored-by: moth <moth@blackhillsinfosec.com>
Co-authored-by: William Stearns <william.l.stearns@gmail.com>
Co-authored-by: William Stearns <3538265+william-stearns@users.noreply.github.com>
Co-authored-by: moth <25512187+0x6d6f7468@users.noreply.github.com>
2025-09-22 16:21:05 -04:00

493 lines
19 KiB
Go

package integration_test
import (
"context"
"slices"
"strings"
"testing"
"time"
"github.com/activecm/rita/v5/cmd"
"github.com/activecm/rita/v5/config"
c "github.com/activecm/rita/v5/constants"
"github.com/activecm/rita/v5/database"
i "github.com/activecm/rita/v5/importer"
"github.com/activecm/rita/v5/util"
fp "path/filepath"
"github.com/ClickHouse/clickhouse-go/v2"
"github.com/spf13/afero"
"github.com/stretchr/testify/require"
)
func CheckImportFileTracking(t *testing.T, importer *i.Importer) { // uses valid dataset
t.Helper()
var result struct {
Count uint64 `ch:"count"`
}
// set context with importID and database parameters
ctx := clickhouse.Context(importer.Database.GetContext(), clickhouse.WithParameters(clickhouse.Parameters{
"importID": importer.ImportID.Hex(),
"database": importer.Database.GetSelectedDB(),
}))
// make sure total (post duplicate filtering) file count to be imported matches file count in metadb
err := importer.Database.Conn.QueryRow(ctx, `
SELECT count() AS count FROM metadatabase.files
WHERE import_id = unhex({importID:String}) AND database = {database:String}
`).ScanStruct(&result)
require.NoError(t, err)
require.EqualValues(t, importer.TotalFileCount, result.Count, "total file count matches imported file count")
var allFiles []string
allFiles = append(allFiles, importer.FileMap[c.ConnPrefix]...)
allFiles = append(allFiles, importer.FileMap[c.OpenConnPrefix]...)
allFiles = append(allFiles, importer.FileMap[c.HTTPPrefix]...)
allFiles = append(allFiles, importer.FileMap[c.OpenHTTPPrefix]...)
allFiles = append(allFiles, importer.FileMap[c.SSLPrefix]...)
allFiles = append(allFiles, importer.FileMap[c.OpenSSLPrefix]...)
allFiles = append(allFiles, importer.FileMap[c.DNSPrefix]...)
var filesResult struct {
Files []string `ch:"files"`
}
// set context with importID parameter
ctx = clickhouse.Context(importer.Database.GetContext(), clickhouse.WithParameters(clickhouse.Parameters{
"importID": importer.ImportID.Hex(),
}))
// make sure all files in created filemap were imported and saved in metadb
err = importer.Database.Conn.QueryRow(ctx, `
SELECT groupArray(path) as files FROM metadatabase.files
WHERE import_id = unhex({importID:String})
`).ScanStruct(&filesResult)
require.NoError(t, err)
require.ElementsMatch(t, allFiles, filesResult.Files, "files in filemap created for import match list of imported files in metadb")
// Make sure duplicate files in same import don't get imported (conn.log & conn.log.gz)
for _, file := range allFiles {
if strings.HasSuffix(file, ".gz") {
hasUncompressedDuplicate := slices.Contains(allFiles, strings.TrimSuffix(file, ".gz"))
require.False(t, hasUncompressedDuplicate, "ucompressed duplicate of .gz file was not imported")
} else {
hasCompressedDuplicate := slices.Contains(allFiles, (file + ".gz"))
require.False(t, hasCompressedDuplicate, "compressed duplicate of uncompressed file was not imported")
}
}
// importing again should fail because all files are imported
err = importer.Import(afero.NewOsFs(), importer.FileMap)
require.Error(t, err)
}
func TestImportTracking(t *testing.T) {
// set up file system interface
afs := afero.NewOsFs()
// load config
cfg, err := config.ReadFileConfig(afs, ConfigPath)
require.NoError(t, err)
// update config with clickhouse connection
cfg.Env.DBConnection = dockerInfo.clickhouseConnection
// ROLLING IMPORT
// new import
_, err = cmd.RunImportCmd(time.Now(), cfg, afs, TestDataPath+"/open_conns/closed", "test_import_rolling", true, false)
require.NoError(t, err, "new rolling import should succeed")
// connect to database
_, err = database.ConnectToDB(context.Background(), "test_import_rolling", cfg, nil)
require.NoError(t, err)
// import another folder
_, err = cmd.RunImportCmd(time.Now(), cfg, afero.NewOsFs(), TestDataPath+"/open_conns/open", "test_import_rolling", true, false)
require.NoError(t, err, "importing another folder to a rolling database should succeed")
// connect to database
_, err = database.ConnectToDB(context.Background(), "test_import_rolling", cfg, nil)
require.NoError(t, err)
// rebuild dataset
_, err = cmd.RunImportCmd(time.Now(), cfg, afero.NewOsFs(), TestDataPath+"/open_conns/open", "test_import_rolling", true, true)
require.NoError(t, err, "importing same folder to a rebuilt rolling database should succeed")
// connect to database
_, err = database.ConnectToDB(context.Background(), "test_import_rolling", cfg, nil)
require.NoError(t, err)
// ***************************************************************************
// NON-ROLLING IMPORT
_, err = cmd.RunImportCmd(time.Now(), cfg, afero.NewOsFs(), TestDataPath+"/open_conns/closed", "test_import_nonrolling", false, true)
require.NoError(t, err, "new non-rolling import should succeed")
// connect to database
_, err = database.ConnectToDB(context.Background(), "test_import_nonrolling", cfg, nil)
require.NoError(t, err)
// import another folder
_, err = cmd.RunImportCmd(time.Now(), cfg, afero.NewOsFs(), TestDataPath+"/open_conns/closed", "test_import_nonrolling", false, false)
require.Error(t, err, "importing another folder to a non-rolling database should not succeed")
// connect to database
_, err = database.ConnectToDB(context.Background(), "test_import_nonrolling", cfg, nil)
require.NoError(t, err)
// rebuild dataset
_, err = cmd.RunImportCmd(time.Now(), cfg, afero.NewOsFs(), TestDataPath+"/open_conns/closed", "test_import_nonrolling", false, true)
require.NoError(t, err, "importing same folder to a rebuilt non-rolling database should succeed")
// connect to database
_, err = database.ConnectToDB(context.Background(), "test_import_nonrolling", cfg, nil)
require.NoError(t, err)
// rebuild dataset & convert to rolling
_, err = cmd.RunImportCmd(time.Now(), cfg, afero.NewOsFs(), TestDataPath+"/open_conns/open", "test_import_nonrolling", true, true)
require.NoError(t, err, "importing once to a non-rolling database converted to a rolling database should succeed")
// connect to database
_, err = database.ConnectToDB(context.Background(), "test_import_nonrolling", cfg, nil)
require.NoError(t, err)
// import again to rolling dataset
_, err = cmd.RunImportCmd(time.Now(), cfg, afero.NewOsFs(), TestDataPath+"/open_conns/closed", "test_import_nonrolling", true, true)
require.NoError(t, err, "importing twice to a converted rolling database should succeed")
// connect to database
_, err = database.ConnectToDB(context.Background(), "test_import_nonrolling", cfg, nil)
require.NoError(t, err)
}
// TestMinMaxTimestamps tests that the min and max timestamps are correctly stored in the metadatabase.imports table
func TestMinMaxTimestamps(t *testing.T) {
// set up file system interface
afs := afero.NewOsFs()
// load config
cfg, err := config.ReadFileConfig(afs, ConfigPath)
require.NoError(t, err)
// update config with clickhouse connection
cfg.Env.DBConnection = dockerInfo.clickhouseConnection
require.True(t, cfg.Filtering.FilterExternalToInternal)
// connect to clickhouse server
server, err := database.ConnectToServer(context.Background(), cfg)
require.NoError(t, err, "connecting to server should not produce an error")
err = server.Conn.Exec(server.GetContext(), "TRUNCATE TABLE IF EXISTS metadatabase.imports")
require.NoError(t, err)
err = server.Conn.Exec(server.GetContext(), "TRUNCATE TABLE IF EXISTS metadatabase.min_max")
require.NoError(t, err)
t.Run("Open Dataset", func(t *testing.T) {
// open dataset
results, err := cmd.RunImportCmd(time.Now(), cfg, afs, TestDataPath+"/open_conns/open", "test_minmax_open", false, true)
require.NoError(t, err, "import should succeed")
// connect to database
db, err := database.ConnectToDB(context.Background(), "test_minmax_open", cfg, nil)
require.NoError(t, err)
minTSBeacon := 1517420070
maxTSBeacon := 1517422419
minTS := 1517336019
maxTS := 1517422419
minOpenTS := 1517336042
maxOpenTS := 1517336223
// validate metadb imports table values for min/max (these are used for troubleshooting)
min, max, minOpen, maxOpen := getMinMaxTimestamps(t, db, results.ImportID[0])
require.EqualValues(t, minTS, min.Unix(), "imports: min timestamp matches expected min timestamp: open_conns/open")
require.EqualValues(t, maxTS, max.Unix(), "imports: max timestamp matches expected max timestamp: open_conns/open")
require.EqualValues(t, minOpenTS, minOpen.Unix(), "imports: min open timestamp matches expected min open timestamp: open_conns/open")
require.EqualValues(t, maxOpenTS, maxOpen.Unix(), "imports: max open timestamp matches expected max open timestamp: open_conns/open")
// validate metadb min_max values for min/max
min, max, notFromConn, useCurrentTime, err := db.GetTrueMinMaxTimestamps()
require.NoError(t, err, "getting true min/max timestamps should not error")
require.EqualValues(t, minTS, min.Unix(), "min_max: min timestamp matches expected min timestamp: open_conns/open")
require.EqualValues(t, maxTS, max.Unix(), "min_max: max timestamp matches expected max timestamp: open_conns/open")
// validate which table min max is from
require.False(t, notFromConn, "min and max timestamps should be from conn table")
require.False(t, useCurrentTime, "first seen analysis should not use the current time")
min, max, notFromConn, err = db.GetBeaconMinMaxTimestamps()
require.NoError(t, err, "getting beacon min/max timestamps should not error")
require.EqualValues(t, minTSBeacon, min.Unix(), "min_max: beacon min timestamp matches expected min timestamp: open_conns/open")
require.EqualValues(t, maxTSBeacon, max.Unix(), "min_max: beacon max timestamp matches expected max timestamp: open_conns/open")
// validate which table min max is from
require.False(t, notFromConn, "min and max timestamps should be from conn table")
})
t.Run("Closed Dataset", func(t *testing.T) {
// closed dataset
results, err := cmd.RunImportCmd(time.Now(), cfg, afs, TestDataPath+"/valid_tsv", "test_minmax", false, true)
require.NoError(t, err, "import should succeed")
// connect to database
db, err := database.ConnectToDB(context.Background(), "test_minmax", cfg, nil)
require.NoError(t, err)
min, max, minOpen, maxOpen := getMinMaxTimestamps(t, db, results.ImportID[0])
require.NoError(t, err, "getting min/max timestamps should not error")
minTS := 1517336040 // isn't 1517336042 because DNS rounds to start of hour
minOpenTS := 1517336042
maxTS := 1517422440
minTSBeacon := 1517336042
maxTSBeacon := 1517422440
// validate metadb imports table values for min/max (these are used for troubleshooting)
require.EqualValues(t, minTS, min.Unix(), "imports: min timestamp matches expected min timestamp: valid_tsv")
require.EqualValues(t, maxTS, max.Unix(), "imports: max timestamp matches expected max timestamp: valid_tsv")
require.EqualValues(t, minOpenTS, minOpen.Unix(), "imports: min open timestamp should be unset: valid_tsv")
require.EqualValues(t, maxTS, maxOpen.Unix(), "imports: max open timestamp should be unset: valid_tsv")
// validate metadb min_max values for min/max
min, max, notFromConn, useCurrentTime, err := db.GetTrueMinMaxTimestamps()
require.NoError(t, err, "getting min/max timestamps should not error")
require.EqualValues(t, minTS, min.Unix(), "min_max: min timestamp matches expected min timestamp: open_conns/open")
require.EqualValues(t, maxTS, max.Unix(), "min_max: max timestamp matches expected max timestamp: open_conns/open")
require.False(t, notFromConn, "min and max timestamps should be from conn table")
require.False(t, useCurrentTime, "first seen analysis should not use the current time")
min, max, notFromConn, err = db.GetBeaconMinMaxTimestamps()
require.NoError(t, err, "getting min/max timestamps should not error")
require.EqualValues(t, minTSBeacon, min.Unix(), "min_max: beacon min timestamp matches expected min timestamp: open_conns/open")
require.EqualValues(t, maxTSBeacon, max.Unix(), "min_max: beacon max timestamp matches expected max timestamp: open_conns/open")
require.False(t, notFromConn, "min and max timestamps should be from conn table")
})
}
func getMinMaxTimestamps(t *testing.T, db *database.DB, importID util.FixedString) (time.Time, time.Time, time.Time, time.Time) { // uses valid dataset
t.Helper()
type minMaxRes struct {
Min time.Time `ch:"min_timestamp"`
Max time.Time `ch:"max_timestamp"`
MinOpen time.Time `ch:"min_open_timestamp"`
MaxOpen time.Time `ch:"max_open_timestamp"`
}
var result minMaxRes
// set context with importID and database parameters
ctx := clickhouse.Context(db.GetContext(), clickhouse.WithParameters(clickhouse.Parameters{
"importID": importID.Hex(),
"database": db.GetSelectedDB(),
}))
// make sure min and max timestamps are correct in the metadatabase.imports record
err := db.Conn.QueryRow(ctx, `
SELECT min_timestamp, max_timestamp, min_open_timestamp, max_open_timestamp FROM metadatabase.imports
WHERE import_id = unhex({importID:String}) AND database = {database:String}
ORDER BY ended_at DESC
`).ScanStruct(&result)
require.NoError(t, err)
return result.Min, result.Max, result.MinOpen, result.MaxOpen
}
func TestMetaDatabase(t *testing.T) {
// set up file system interface
afs := afero.NewOsFs()
// load config
cfg, err := config.ReadFileConfig(afs, ConfigPath)
require.NoError(t, err)
cfg.Env.DBConnection = dockerInfo.clickhouseConnection
// import a dataset
dbName := "test_metadb"
results, err := cmd.RunImportCmd(time.Now(), cfg, afs, TestDataPath+"/valid_tsv", dbName+"_tsv", false, true)
require.NoError(t, err, "import should succeed")
importID := results.ImportID[0]
// import a few other datasets for testing multiple metadb entries
_, err = cmd.RunImportCmd(time.Now(), cfg, afs, TestDataPath+"/valid_json", dbName+"_json", false, true)
require.NoError(t, err, "import should succeed")
_, err = cmd.RunImportCmd(time.Now(), cfg, afs, TestDataPath+"/open_conns/open", dbName+"_open", false, true)
require.NoError(t, err, "import should succeed")
// connect to metadatabase
db, err := database.ConnectToDB(context.Background(), "metadatabase", cfg, nil)
require.NoError(t, err)
// test metadatabase tables
t.Run("Imports Table", func(t *testing.T) {
validateMetaDBImportsTable(t, db, dbName+"_tsv", importID)
})
t.Run("Files Table", func(t *testing.T) {
validateMetaDBFilesTable(t, db, TestDataPath+"/valid_tsv", dbName+"_tsv", importID)
})
validateMetaDBHistoricalFirstSeenTable(t)
validateMetaDBValidMimeTypesTable(t)
}
func validateMetaDBImportsTable(t *testing.T, db *database.DB, dbName string, importID util.FixedString) {
t.Helper()
// set the context
ctx := clickhouse.Context(db.GetContext(), clickhouse.WithParameters(clickhouse.Parameters{
"table": "imports",
"database": dbName,
"importID": importID.Hex(),
}))
var result struct {
Count uint64 `ch:"count"`
}
// imports table exists
t.Run("Table Exists", func(t *testing.T) {
var exists uint8
err := db.Conn.QueryRow(ctx, `EXISTS TABLE {table:Identifier}`).Scan(&exists)
require.NoError(t, err)
require.EqualValues(t, 1, exists, "imports table exists")
})
// verify that start and end import records for first import were created
t.Run("Record Matches", func(t *testing.T) {
err := db.Conn.QueryRow(ctx, `
SELECT count() AS count
FROM {table:Identifier}
WHERE database = {database:String} AND import_id = unhex({importID:String})
`).ScanStruct(&result)
require.NoError(t, err)
// 2 import records should be created, one at the start of the import and one when the import is finished
require.EqualValues(t, 2, result.Count, "expected 2 import records to be created, got %d", result.Count)
})
// no import record has unset import_id
t.Run("No Unset ImportID", func(t *testing.T) {
err := db.Conn.QueryRow(ctx, `
SELECT count() AS count
FROM {table:Identifier}
WHERE import_id==toFixedString('',16) OR hex(import_id)=='00000000000000000000000000000000' OR import_id=='' OR import_id IS NULL
`).ScanStruct(&result)
require.NoError(t, err)
require.EqualValues(t, 0, result.Count, "imports table must not have unset import_id fields")
})
// no import record has unset database
t.Run("No Unset Database", func(t *testing.T) {
err := db.Conn.QueryRow(ctx, `
SELECT count() AS count
FROM {table:Identifier}
WHERE database=='' OR database IS NULL
`).ScanStruct(&result)
require.NoError(t, err)
require.EqualValues(t, 0, result.Count, "imports table must not have unset database fields")
})
}
func validateMetaDBFilesTable(t *testing.T, db *database.DB, directory string, dbName string, importID util.FixedString) {
t.Helper()
// set the context
ctx := clickhouse.Context(db.GetContext(), clickhouse.WithParameters(clickhouse.Parameters{
"table": "files",
"database": dbName,
"importID": importID.Hex(),
}))
// files table exists
t.Run("Table Exists", func(t *testing.T) {
var exists uint8
err := db.Conn.QueryRow(ctx, `EXISTS TABLE {table:Identifier}`).Scan(&exists)
require.NoError(t, err)
require.EqualValues(t, 1, exists, "files table must exist")
})
// verify that files entries for import are correct
t.Run("Files List Correct", func(t *testing.T) {
// get the files listed in the files table for the first import
rows, err := db.Conn.Query(ctx, `
SELECT path
FROM {table:Identifier}
WHERE database = {database:String} AND import_id = unhex({importID:String})
`)
require.NoError(t, err)
defer rows.Close()
// type fileVersions
// map to track the versions of each file
dbFiles := make(map[string]struct {
log bool
logGz bool
})
for rows.Next() {
var result struct {
Path string `ch:"path"`
}
err = rows.ScanStruct(&result)
require.NoError(t, err)
// get the filename without the path
name := fp.Base(result.Path)
// set map key as the filename without the .gz extension
normalizedFilename := strings.TrimSuffix(name, ".gz")
// get the version info for the file
versionInfo := dbFiles[normalizedFilename]
// set the version info for the file
if strings.HasSuffix(name, ".gz") {
versionInfo.logGz = true
} else {
versionInfo.log = true
}
// update the map
dbFiles[normalizedFilename] = versionInfo
}
// get all files in the directory
fs := afero.NewOsFs()
allFiles, err := afero.ReadDir(fs, directory)
require.NoError(t, err)
// check that a single version (log or log.gz) of each file is present in the database
for _, file := range allFiles {
normalizedFileName := strings.TrimSuffix(file.Name(), ".gz")
if normalizedFileName == ".DS_Store" {
continue
}
versions, exists := dbFiles[normalizedFileName]
// check that a version of the file exists in the database
require.True(t, exists, "file must exist in database records: %s", normalizedFileName)
// verify that only one version of the file is present in the database
require.False(t, versions.log && versions.logGz, "both .log and .log.gz versions cannot be present in the database")
}
})
}
func validateMetaDBHistoricalFirstSeenTable(t *testing.T) {
t.Helper()
}
func validateMetaDBValidMimeTypesTable(t *testing.T) {
t.Helper()
}