Artifacts (file inventory)¶
The artifact source builds an inventory of files that other sources have seen. When a process starts, a module loads, or a file is created or written, FreeDR opens the file read-only and records its identity, size, timestamps, SHA-256, PE header values, Authenticode verdict and alternate data stream names.
| Collector ID | freedr.artifact (health: artifact-inventory) |
| Windows mechanism | Cooperative read-only open (NtCreateFile with FILE_OPEN_REQUIRING_OPLOCK and a Read-Handle oplock, sharing read/write/delete), WinVerifyTrust, GetFileInformationByHandleEx |
| event_type | artifact.content_version (the only one) |
| Hunt | events view, event_type = 'artifact.content_version' |
| Rule SQL source name | artifact (plus artifact_path_state for "latest version of this path") |
| Hot tables | artifact_v5 (history), artifact_path_state_v1 (one row per path) |
| Code | crates/freedr-collect-artifact |
FreeDR does not copy, quarantine or upload files. It reads the file body only to compute the hash.
What triggers acquisition¶
| Trigger | Source events | acquisition_reason |
|---|---|---|
| Event-driven | process.start and image.load (image path); file.create_open, file.write, file.rename, file.set_info; every retained usn.change |
1 (Event) |
| Live MFT Hunt with enrich artifact | Paths the user looks up | 3 (LiveMft) |
| Case enrichment: file hash | Alert and case enrichment needs a hash | 3 (LiveMft) |
| Case enrichment: file signature | Returns a verdict only and writes no row | — |
These do not trigger acquisition: process.exit, file.delete, registry,
Windows Event Log, and artifact rows themselves. System32 DLL loads do not
trigger either, because the process source never stores them. Reason 2
(Detection) is reserved and unused. There is no baseline scan of existing
files.
Repeat collection¶
| Rule | Detail |
|---|---|
| New path | The first trigger for a path not in artifact_path_state_v1 is collected |
| Known path | Collected again only on a content change: file.write, or a USN reason in data overwrite/extend/truncation (also named streams), file create or stream change. Opens, process starts, image loads, renames and set-info do not re-collect. |
| Path key | / becomes \, \\?\ is stripped from drive paths, and the path is ASCII upper-cased. Hard links are separate paths. |
| Unchanged content | Produces the same deterministic event_id, so no new row |
| Path-state expiry | Path-state rows older than retention_days are pruned. The next trigger then counts as new. |
| Failure suppression | A path that failed as not_found, access_denied, path_mismatch or ineligible is not retried by events for 10 minutes (2,048 paths). Targeted requests are never suppressed. |
What is recorded (artifact_v5)¶
| Column | Meaning |
|---|---|
occurred_at |
File LastWriteTime, not the acquisition time |
path |
Final normalized path from the open handle |
volume_serial, file_id |
64-bit volume serial and 128-bit NTFS file ID |
file_size |
End-of-file size |
created_at, modified_at |
CreationTime and LastWriteTime. Both are kept, so timestomping is visible. |
sha256 |
SHA-256 of the main (unnamed) data stream. NULL only for files over 500 MiB. |
authenticode_status |
1 trusted, 2 invalid, 3 unsigned, 4 unknown / not checked |
signer |
Certificate subject (up to 512 bytes), only for status 1 or 2. For 2 it is an unverified claim. |
ads |
List of named $DATA streams (name, size). [] = none, NULL = unknown or truncated (more than 64 streams or names over 1,024 bytes). Stream content, including Zone.Identifier ZoneId/HostUrl, is not read. |
pe_image_size, pe_checksum, pe_timestamp, pe_preferred_base |
From the PE headers (first 1 MiB). NULL if the file is not a valid PE. |
acquisition_reason |
1 Event, 3 LiveMft (2 reserved) |
Not recorded: MD5, SHA-1, imphash, fuzzy hashes, version information (product name, original file name), sections and imports.
Signature verification¶
- Only valid PE files up to 500 MiB are checked.
WinVerifyTrustruns with no revocation check and cache-only URL retrieval. It makes no network access.- If there is no embedded signature, the local catalog is checked (SHA-256, then SHA-1).
- HRESULT mapping: success → trusted;
TRUST_E_NOSIGNATURE→ unsigned; certificate or signature errors (expired, revoked, bad digest, untrusted root, …) → invalid; anything else → unknown. - Store/MSIX package files without an embedded or catalog signature are reported as unknown, not unsigned.
- Definite verdicts are cached in memory by SHA-256 (1,024 entries, 10 minutes).
Large files¶
Files over 500 MiB are not read. A row is still written with size,
timestamps and ADS, but sha256, the PE columns and signer are NULL and
authenticode_status is 4. Hash searches and hash-based rules do not match
these rows.
Why a file is not collected¶
Rejected before queueing¶
| Reason | When |
|---|---|
empty_path |
Empty path |
unsupported_path |
Over 32 KiB, invalid characters, a component ending in . or a space, or a device name (CON, NUL, COM1, …) |
network_path |
UNC \\server\… |
relative_path |
Not X:\…. This includes NT/device paths. |
directory |
Path is a directory or root |
unresolved_path |
./.. components or an unexpanded %VAR% |
unsupported_volume |
Not a fixed local NTFS volume |
excluded_path |
Under C:\ProgramData\FreeDR, C:\Windows\Logs\CBS\CBS.log, the agent machine root, or configured excluded roots (max 128) |
invalid_host, invalid_identity |
Internal identity errors |
Skipped after queueing (health counters)¶
| Counter | Cause | 10-min retry suppression |
|---|---|---|
not_found |
File or path gone | Yes |
access_denied |
Access denied | Yes |
sharing_violation |
File locked | No |
path_mismatch |
File at the path changed identity | Yes |
ineligible |
Directory, reparse point, offline/cloud placeholder (recall on open/access), delete pending, no read lease, no file version | Yes |
interrupted |
Oplock break or file changed while reading. The partial result is discarded. | No |
Other errors count in acquisition_errors and per cause in
acquisition_error_<operation>:<code>. These are informational and do not
mark health degraded.
Throughput and limits¶
| Item | Value |
|---|---|
| Queue | 16,384 requests ([artifact] control_capacity, 1–16,384). When full, the request is dropped and counted in capacity_drops. Ingestion is never blocked. |
| Coalescing | One pending request per path |
| Workers | 1–8, autoscaled. An idle worker retires after 2 s. |
| I/O rate limit | None |
| Commit | Batches of up to 32 rows or 100 ms through the single database writer. Failed batches are retried per row 3 times, then commit_drops. |
| Durability | Queued requests and unflushed rows are lost if the agent stops |
Health (artifact-inventory)¶
Counters: queued, coalesced, excluded, rejected_format,
rejected_queued, signatures, targeted_failures, suppressed,
not_found, access_denied, sharing_violation, path_mismatch,
ineligible, interrupted, dropped, capacity_drops,
acquisition_errors, commit_drops, acquired, committed,
write_failures, lookup_failures, coordinator_*. Gauges:
control_capacity, pending_ram, completed_batch_ram.
The collector is degraded while capacity_drops, commit_drops,
write_failures or lookup_failures are growing, then held for 30 minutes
(see degraded hold).
Not available
Prefetch, Amcache, Shimcache, SRUM, autoruns and similar host artifact parsers are roadmap items. They are not part of this source.