yara stuff

This commit is contained in:
Johannes Bülow 2025-07-23 13:19:28 +02:00
parent 5d05e59b42
commit 8de46a3682
Signed by: jmb
GPG key ID: B56971CF7B8F83A6
10 changed files with 209 additions and 4 deletions

View file

@ -3,7 +3,6 @@ package config
import ( import (
"log/slog" "log/slog"
_ "github.com/joho/godotenv/autoload"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@ -47,7 +46,9 @@ func setDefaults() {
// Others // Others
viper.SetDefault("processing.oleurl", "http://localhost:5000") viper.SetDefault("processing.oleurl", "http://localhost:5000")
viper.SetDefault("processing.maxmimesize", "100MB") viper.SetDefault("processing.maxmimesize", "100MB")
viper.SetDefault("store.path", "./storage/files/") viper.SetDefault("processing.yararules", "./storage/rules")
viper.SetDefault("processing.yaracompiled", "./storage/output.yarc")
viper.SetDefault("store.path", "./storage/files")
viper.SetDefault("debug", false) viper.SetDefault("debug", false)
// UI Interface info // UI Interface info
viper.SetDefault("ui.name", "Scanfile") viper.SetDefault("ui.name", "Scanfile")

View file

@ -0,0 +1,14 @@
-- name: InsertYaraResults :one
INSERT INTO yara_results (
file_id, matched
) VALUES ($1, $2)
RETURNING *;
-- name: GetYaraResults :one
SELECT * FROM yara_results
WHERE file_id = $1
LIMIT 1;
-- name: DeleteYaraResults :exec
DELETE FROM yara_results
WHERE id = $1;

View file

@ -22,7 +22,7 @@ CREATE TABLE IF NOT EXISTS processing_jobs (
status TEXT, status TEXT,
job_type TEXT, job_type TEXT,
error TEXT, error TEXT,
messages JSONB DEFAULT '[]'::JSONB messages TEXT[]
); );
CREATE TABLE IF NOT EXISTS diec ( CREATE TABLE IF NOT EXISTS diec (
@ -77,6 +77,12 @@ CREATE TABLE IF NOT EXISTS file_properties (
libmagic_apple TEXT libmagic_apple TEXT
); );
CREATE TABLE IF NOT EXISTS yara_results (
id BIGSERIAL PRIMARY KEY,
file_id UUID REFERENCES files (id) ON DELETE CASCADE,
matched TEXT[]
);
-- Indices -- Indices
-- Since tables will be heavily accessed by file_id, there should be indices for them -- Since tables will be heavily accessed by file_id, there should be indices for them
CREATE INDEX idx_diec_file_id ON diec (file_id); CREATE INDEX idx_diec_file_id ON diec (file_id);

View file

@ -0,0 +1,33 @@
package database
import (
"context"
"log/slog"
"git.jmbit.de/jmb/scanfile/server/internal/sqlc"
"github.com/jackc/pgx/v5/pgtype"
)
func GetYaraResults(fileID string) (sqlc.YaraResult, error) {
var pgUUID pgtype.UUID
err := pgUUID.Scan(fileID)
if err != nil {
slog.Error("Unable to convert string to UUID", "file-uuid", fileID, "error", err)
}
query := sqlc.New(pool)
data, err := query.GetYaraResults(context.Background(), pgUUID)
if err != nil {
slog.Error("Error in GetMsofficeInfo", "file-uuid", fileID, "error", err)
}
return data, err
}
func InsertYaraResults(params sqlc.InsertYaraResultsParams) error {
query := sqlc.New(pool)
slog.Debug("InsertYaraResults", "params", params)
_, err := query.InsertYaraResults(context.Background(), params)
if err != nil {
slog.Error("Error in InsertYaraResults", "file-uuid", params.FileID.String(), "error", err)
}
return err
}

View file

@ -9,6 +9,7 @@ import (
"git.jmbit.de/jmb/scanfile/server/internal/database" "git.jmbit.de/jmb/scanfile/server/internal/database"
"git.jmbit.de/jmb/scanfile/server/internal/processing/basic" "git.jmbit.de/jmb/scanfile/server/internal/processing/basic"
"git.jmbit.de/jmb/scanfile/server/internal/processing/msoffice" "git.jmbit.de/jmb/scanfile/server/internal/processing/msoffice"
"git.jmbit.de/jmb/scanfile/server/internal/processing/yara"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
) )
@ -17,6 +18,7 @@ var startup time.Time
func Setup(wg *sync.WaitGroup) { func Setup(wg *sync.WaitGroup) {
startup = time.Now() startup = time.Now()
yara.InitYara()
} }
// Submit() starts the analysis process for a file. // Submit() starts the analysis process for a file.

View file

@ -0,0 +1,56 @@
package yara
import (
"log/slog"
"os/exec"
"path/filepath"
"git.jmbit.de/jmb/scanfile/server/internal/store"
"github.com/spf13/viper"
)
func compileSourcesFromFiles() error {
root, err := filepath.Abs(viper.GetString("processing.yararules"))
if err != nil {
slog.Error("Error getting absolute path for processing.yararules", "error", err)
return err
}
outputPath, err := filepath.Abs(viper.GetString("processing.yaracompiled"))
if err != nil {
slog.Error("Error getting absolute path for processing.yaracompiled", "error", err)
return err
}
cmd := exec.Command("/usr/local/bin/yr", "compile","-path-as-namespace", "--relaxed-re-syntax", "--output", outputPath, root)
result, err := cmd.Output()
if err != nil {
slog.Error("Error compiling yara rules", "error", err, "result", string(result))
return err
} else {
slog.Info("Compiled yara rules", "result", string(result))
}
return nil
}
func scanFile(fileName string) ([]string, error) {
var matched []string
outputPath, err := filepath.Abs(viper.GetString("processing.yaracompiled"))
if err != nil {
slog.Error("Error getting absolute path for processing.yaracompiled", "error", err)
return matched, err
}
fullPath, err := store.AbsPath(fileName)
if err != nil {
slog.Error("Error in DiecScan", "file-uuid", fileName, "error", err)
return matched, err
}
cmd := exec.Command("/usr/local/bin/yr", "scan", "--output-format ndjson", "--print-namespace","--compiled-rules", outputPath, fullPath)
result, err := cmd.Output()
if err != nil {
slog.Error("Error scanning file with yara", "error", err, "file-uuid", fileName,"result", string(result))
return matched, err
}
return matched, nil
}

View file

@ -0,0 +1,32 @@
package yara
import (
"git.jmbit.de/jmb/scanfile/server/internal/database"
"git.jmbit.de/jmb/scanfile/server/internal/sqlc"
)
func InitYara() error{
return compileSourcesFromFiles()
}
func YaraProcessing(job sqlc.ProcessingJob) error {
database.StartProcessingJob(job.ID)
results, err := scanFile(job.FileID.String())
if err != nil {
database.FailProcessingJob(job.ID, err)
return err
}
params := sqlc.InsertYaraResultsParams{
FileID: job.FileID,
Matched: results,
}
err = database.InsertYaraResults(params)
if err != nil {
database.FailProcessingJob(job.ID, err)
return err
}
return nil
}

View file

@ -79,5 +79,11 @@ type ProcessingJob struct {
Status pgtype.Text Status pgtype.Text
JobType pgtype.Text JobType pgtype.Text
Error pgtype.Text Error pgtype.Text
Messages []byte Messages []string
}
type YaraResult struct {
ID int64
FileID pgtype.UUID
Matched []string
} }

View file

@ -0,0 +1,54 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: queries-yara.sql
package sqlc
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const deleteYaraResults = `-- name: DeleteYaraResults :exec
DELETE FROM yara_results
WHERE id = $1
`
func (q *Queries) DeleteYaraResults(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, deleteYaraResults, id)
return err
}
const getYaraResults = `-- name: GetYaraResults :one
SELECT id, file_id, matched FROM yara_results
WHERE file_id = $1
LIMIT 1
`
func (q *Queries) GetYaraResults(ctx context.Context, fileID pgtype.UUID) (YaraResult, error) {
row := q.db.QueryRow(ctx, getYaraResults, fileID)
var i YaraResult
err := row.Scan(&i.ID, &i.FileID, &i.Matched)
return i, err
}
const insertYaraResults = `-- name: InsertYaraResults :one
INSERT INTO yara_results (
file_id, matched
) VALUES ($1, $2)
RETURNING id, file_id, matched
`
type InsertYaraResultsParams struct {
FileID pgtype.UUID
Matched []string
}
func (q *Queries) InsertYaraResults(ctx context.Context, arg InsertYaraResultsParams) (YaraResult, error) {
row := q.db.QueryRow(ctx, insertYaraResults, arg.FileID, arg.Matched)
var i YaraResult
err := row.Scan(&i.ID, &i.FileID, &i.Matched)
return i, err
}

View file

@ -8,6 +8,7 @@ sql:
- "server/internal/database/queries-file_properties.sql" - "server/internal/database/queries-file_properties.sql"
- "server/internal/database/queries-processing_jobs.sql" - "server/internal/database/queries-processing_jobs.sql"
- "server/internal/database/queries-msoffice.sql" - "server/internal/database/queries-msoffice.sql"
- "server/internal/database/queries-yara.sql"
database: database:
managed: false managed: false
uri: "postgresql://scanfile:${PG_PASSWORD}@localhost:5432/scanfile" uri: "postgresql://scanfile:${PG_PASSWORD}@localhost:5432/scanfile"