FileInspectorX: dependency-free content type detection (magic bytes + heuristics)
License
—
Deps
10
Install Size
—
Vulns
✓ 0
Published
Jan 20, 2026
$ dotnet add package FileInspectorX📦 NuGet (Library)
💻 PowerShell Module
🛠️ Project Information
👨💻 Author & Social
FileInspectorX is a library for content type detection (magic bytes + heuristics) and lightweight analysis (containers, PDFs, PE triage, text/script cues) across .NET 8, .NET Standard 2.0 and .NET Framework 4.7.2. A thin PowerShell module provides pipeline‑friendly cmdlets with reusable typed views.
dotnet add package FileInspectorX
Install-Module FileInspectorX.PowerShell
# or local build
Import-Module .\FileInspectorX.PowerShell\bin\Debug\net8.0\FileInspectorX.PowerShell.dll
using FileInspectorX;
var options = new FileInspector.DetectionOptions { ComputeSha256 = true, MagicHeaderBytes = 16 };
var analysis = FileInspector.Analyze(path, options);
Console.WriteLine($"{analysis.Detection?.Extension} {analysis.Detection?.MimeType} {analysis.Kind} {analysis.Flags}");
// Flatten for tabular/log display
var summary = SummaryView.From(path, analysis);
var perms = PermissionsView.From(path, analysis.Security);
var sig = SignatureView.From(path, analysis.Authenticode);
// Detection only
var detOnly = FileInspector.Detect(path, options);
// Presentation-ready report + map
var analysis = FileInspector.Analyze(path);
var report = ReportView.From(analysis);
var map = report.ToDictionary();
// Humanize flags if you only have CSV
string flagsShort;
if (!map.TryGetValue("AnalysisFlagsHuman", out var human))
{
map.TryGetValue("AnalysisFlags", out var csv);
flagsShort = Legend.HumanizeFlagsCsv(csv?.ToString());
}
else flagsShort = human?.ToString() ?? string.Empty;
// Render a Markdown report (dependency-free)
var md = MarkdownRenderer.From(analysis);
// Include file system metadata + flattened dictionary
var summary2 = FileInspector.InspectWithMetadata(path, new FileInspector.DetectionOptions { MagicHeaderBytes = 64 });
var metadata = summary2.Metadata;
// Optional: top tokens for scripts/logs (disabled by default)
Settings.TopTokensEnabled = true;
Settings.TopTokensMax = 8;
Settings.TopTokensMinLength = 4;
Settings.TopTokensMinCount = 2;
// Compare declared vs detected (with alternatives)
var cmp = FileInspector.CompareDeclaredDetailed(".log", summary2.Analysis.Detection);
Console.WriteLine($"Mismatch? {cmp.Mismatch} Reason={cmp.Reason}");
// Consume typed legends directly
foreach (var entry in Legend.GetAnalysisFlagLegend())
Console.WriteLine($"{entry.Short} = {entry.Long}");
# Raw is default (full object)
Get-FileInsight -Path .\file.bin | Format-List
# Opt-in compact views
Get-FileInsight -Path .\file.bin -View Summary | Format-Table -Auto
# Other views: Summary | Detection | Permissions | Signature | Analysis | ShellProperties
Get-FileInsight -Path .\file.bin -View Detection | Format-Table -Auto
Get-FileInsight -Path .\file.bin -View Permissions | Format-Table Path,Owner,Group,ModeSymbolic,EveryoneWriteAllowed -Auto
Get-FileInsight -Path .\app.exe -View Signature | Format-List
Get-FileInsight -Path .\package.msix -View Installer | Format-Table -Auto
Get-FileInsight -Path .\song.mp3 -View ShellProperties | Format-Table -Auto
# Exclude sections to trim work/shape
Get-FileInsight -Path .\file.bin -ExcludePermissions -ExcludeReferences -ExcludeShellProperties | Format-List
# Back-compat: detection only
Get-FileInsight -Path .\file.bin -DetectOnly | Format-List
# Discover parameters and examples
Get-Help Get-FileInsight -Detailed
Get-Help Get-FileInsight -Examples
# Detect-only for all EXE files under the current directory
Get-ChildItem -Filter *.exe -File -Recurse | Get-FileInsight -View Detection | Format-Table -Auto
# Summarize a directory, skipping signature and installer enrichment
Get-ChildItem -File -Recurse | Get-FileInsight -View Summary -ExcludeSignature -ExcludeInstaller | Format-Table -Auto
Default Raw
FileAnalysis object by default (-View Raw). This is a single, rich object with Detection, Flags, Security, Authenticode/Installer, References, Assessment, Secrets summary, and more.Views for display
Use -View Summary|Detection|Analysis|Permissions|Signature|References|Assessment|Installer|ShellProperties to get compact, tabular shapes.
.Raw so you can drill back into the full object without re‑invoking.Trim work/shape with excludes
-ExcludePermissions, -ExcludeSignature, -ExcludeInstaller, -ExcludeReferences, -ExcludeContainer, -ExcludeAssessment, -ExcludeShellProperties.new FileInspector.DetectionOptions { IncludePermissions = false, ... }.var lean = FileInspector.Analyze(path, new FileInspector.DetectionOptions {
IncludePermissions = false,
IncludeReferences = false,
IncludeShellProperties = false,
IncludeAuthenticode = true,
IncludeAssessment = true
});
Pick the API based on how much work you want the library to do and how much metadata you need:
Detect(path[, options]) → ContentTypeDetectionResult
Inspect(path[, options]) → FileAnalysis (single entry point)
Analyze(path[, options]) → FileAnalysis
Examples (C#)
// Detection only (fast)
var det = FileInspector.Detect(path, new FileInspector.DetectionOptions { MagicHeaderBytes = 16 });
// Single entry point (detect-only)
var detFa = FileInspector.Inspect(path, new FileInspector.DetectionOptions { DetectOnly = true });
// Full analysis
var full = FileInspector.Inspect(path, new FileInspector.DetectionOptions { ComputeSha256 = true });
var summary = full.ToSummaryView(path);
Detection runs in a stable order so downstream callers can reason about outcomes:
Declared extension is used as a bias only for ambiguous/low-confidence cases:
Detect(path) uses the file extension as the declared value automatically.Detect(stream, options, declaredExtension) and Detect(ReadOnlySpan<byte>, options, declaredExtension) let you supply the declared extension to match path-based behavior.CompareDeclared normalizes leading dots and can fall back to GuessedExtension when Extension is empty (e.g., ZIP subtypes).CompareDeclaredDetailed returns the mismatch plus reasoning and detection alternatives (when available).The library can extract generic references from common configuration files so consumers can validate what will execute or be accessed. This is domain‑agnostic and useful for many scenarios (build pipelines, endpoint hygiene, or pre‑upload checks).
<Exec><Command>, <Arguments>, <WorkingDirectory> and <ClassId> (COM handler). Flags unquoted paths, UNC usage, relative paths.nCmd and nParameters values, extracts paths and URLs.PowerShell
# List references from a Task XML
Get-FileInsight -Path .\Task.xml -View References | Format-Table -Auto
# List references in a GPO scripts.ini
Get-FileInsight -Path .\scripts.ini -View References | Format-Table -Auto
C#
var a = FileInspector.Analyze(path);
foreach (var r in a.References ?? Array.Empty<Reference>())
{
Console.WriteLine($"{r.Kind} {r.Value} exists={r.Exists} issues={r.Issues}");
}
C# (Assessment)
var full = FileInspector.Analyze(path);
var assess = FileInspector.Assess(full);
Console.WriteLine($"Score={assess.Score} Decision={assess.Decision} Codes={string.Join(" ", assess.Codes)}");
// Factors show score contributions per code (explainability)
foreach (var kv in assess.Factors)
{
Console.WriteLine($"{kv.Key} => {kv.Value}");
}
PowerShell (Assessment)
Get-FileInsight -Path .\file.bin -View Assessment | Format-Table -Auto
This stays generic by design. A higher‑level component (e.g., TierBridge) can layer policy (allow/warn/block) using these references and the existing Flags/SecurityFindings.
When Settings.DeepContainerScanEnabled = true, FileInspectorX can sample a bounded number of inner executables in ZIP archives to provide a quick signer summary without fully extracting archives. The following fields are exposed on FileAnalysis and surfaced via ReportView:
InnerExecutablesSampled: how many inner EXE/DLLs were sampled (bounded by DeepContainerMaxEntries and DeepContainerMaxEntryBytes).InnerSignedExecutables: how many of those were Authenticode‑signed.InnerValidSignedExecutables: how many had a valid chain or passed WinVerifyTrust (when available).InnerPublisherCounts: top publishers (SignerSubjectCN) with counts.Notes:
var a = FileInspector.Analyze(path);
if (a.InnerExecutablesSampled > 0)
{
Console.WriteLine($"Inner execs: {a.InnerExecutablesSampled}, signed: {a.InnerSignedExecutables}, valid: {a.InnerValidSignedExecutables}");
if (a.InnerPublisherCounts != null)
foreach (var kv in a.InnerPublisherCounts.OrderByDescending(kv => kv.Value).Take(5))
Console.WriteLine($" {kv.Key}: {kv.Value}");
}
Use ReportView.From(FileAnalysis) for presentation‑ready fields:
DetectedTypeFriendly: a human‑friendly label (e.g., “Word document”, “ZIP archive”, “X.509 certificate”).FlagsHumanShort/Long: legend‑based humanization of flags and heuristics.AssessmentCodesHuman: humanized assessment codes.These are exported via ReportView.ToDictionary() for templating/email systems.
Example keys: DetectedTypeExtension, DetectedTypeName, DetectionConfidence, CompanyName, ProductName, FileVersion, AnalysisFlags, AnalysisFlagsHuman, AssessmentScore, AssessmentDecision, AssessmentCodes, EncryptedEntryCount, InnerFindings, TopTokens.
DetectionScoreAdjustments defaults to ConcurrentDictionary. If you replace it with a non-thread-safe dictionary, you are responsible for synchronization.// Deep container scanning (bounded)
Settings.DeepContainerScanEnabled = true; // default: false
Settings.DeepContainerMaxEntries = 64; // sample cap
Settings.DeepContainerMaxEntryBytes = 262144; // per‑entry cap (256 KB)
Settings.KnownToolNameIndicators = new[] { "pingcastle", "bloodhound" };
Settings.KnownToolHashes = new Dictionary<string,string> { /* name => lowercase sha256 */ };
// Script & secrets scanning
Settings.SecurityScanScripts = true; // default: true
Settings.SecretsScanEnabled = true; // default: true
// Authenticode (Windows policy optional)
Settings.VerifyAuthenticodeWithWinTrust = true; // default true on Windows
Settings.VerifyAuthenticodeRevocation = false; // default
// Vendor allow hints (for assessment)
Settings.AllowedVendors = new[] { "Microsoft", "YourCompany" };
Settings.VendorMatchMode = VendorMatchMode.Contains; // or Exact
Archive safety
Office / OOXML
PE hardening
Signatures
Vendor / Package
Scripts & secrets (neutral categories)
flowchart TD
A["Input path(s)"] --> B{"Detect (magic + heuristics)"}
B -->|known| C["ContentTypeDetectionResult"]
B -->|unknown| C
C --> D{"Analyze (Include* flags)"}
D --> D1["Container triage (ZIP/TAR subtype, safety, nested)"]
D --> D2["Text/Script heuristics + Secrets"]
D --> D3["Permissions / Ownership"]
D --> D4["Signatures + Installer metadata"]
D --> D5["PDF / OOXML flags"]
D --> D6["Name / Path analysis"]
D --> E["FileAnalysis"]
E --> F{"IncludeAssessment?"}
F -->|Yes| G["Assess() Score + Codes + Factors"]
F -->|No| H["Return FileAnalysis"]
G --> I["Decision: Allow / Warn / Block"]
I --> J[["Views (optional)"]]
H --> J
subgraph Toggles
T1["IncludeContainer"]
T2["IncludePermissions"]
T3["IncludeAuthenticode"]
T4["IncludeInstaller"]
T5["IncludeReferences"]
T6["IncludeAssessment"]
end
T1 -.-> D1
T2 -.-> D3
T3 -.-> D4
T4 -.-> D4
T5 -.-> D2
T6 -.-> G
pie showData
title Example Score Composition
"Archive.PathTraversal (+40)" : 40
"Office.RemoteTemplate (+25)" : 25
"Secret.PrivateKey (+40)" : 40
"Sig.VendorAllowed (−10)" : 10
Notes
GitHub's Mermaid version may not support the new chart directive yet, so here's a pie fallback and a simple table.
pie showData
title Scenario Scores (Illustrative)
"Plain PDF (0)" : 0
"PDF + JS (20)" : 20
"DOCX + Macros (30)" : 30
"DOCX + RemoteTpl (55)" : 55
"ZIP with Executable (25)" : 25
"MSI + CustomAction (50)" : 50
Scenario Score
------------------------ -----
Plain PDF 0
PDF + JS 20
DOCX + Macros 30
DOCX + Remote Template 55
ZIP with Executable 25
MSI with Custom Action 50
These values are illustrative only — actual scores depend on which signals are present and the configured weights.
The full object (Raw/FileAnalysis) exposes Secrets with category counts only — no values are stored:
$fa = Get-FileInsight -Path .\script.ps1
$fa.Secrets | Format-List
var fa = FileInspector.Analyze(path);
var s = fa.Secrets;
Console.WriteLine($"privKeys={s?.PrivateKeyCount} jwt={s?.JwtLikeCount} keyPat={s?.KeyPatternCount}");
stat where availabledotnet build FileInspectorX/FileInspectorX.csprojdotnet testdotnet build FileInspectorX.PowerShell (then Import-Module the built DLL)stat when available (fields may be null; mode bits still populate)