Scanner Language Analyzer Review - 2026-05-06
Summary
Status: completed review with follow-up implementation now tracked in docs-archive/implplan/SPRINT_20260506_004_Scanner_first_class_detector_corrections.md and docs/implplan/SPRINT_20260506_005_Scanner_language_detector_uplift_program.md.
Scope covered the shared Scanner language analyzer core plus Bun, Deno, DotNet, Go, Java, Node, PHP, Python, Ruby, and Rust.
Evidence summary:
- Source builds: 11/11 analyzer projects built successfully with
dotnet buildagainst individual.csprojfiles. - Targeted tests: 12/12 test projects passed against individual
.csprojfiles, 2,082 total tests passed, 0 failed, 0 skipped. - Smoke harness: Python passed; Rust failed during plugin execution because the copied restart plugin directory lacked
Tomlyn.dll. - External web: none used.
Top risks recorded by the initial review:
- HIGH: Restart plugin packaging is incomplete for analyzers with package dependencies. Rust smoke fails with missing
Tomlyn;src/Directory.Build.targets:86copies only the target dll, pdb, deps file when present, and manifest, not copy-local dependency assemblies. - HIGH: Java has a manifest but no
ILanguageAnalyzerPluginimplementation. The shared plugin catalog loadsILanguageAnalyzerPlugintypes, so Java is unlikely to restart-load through the language analyzer plugin catalog. - HIGH: PHP has an
ILanguageAnalyzerPluginimplementation but nomanifest.json, while offline-kit docs promise a PHP plugin directory with manifest. - HIGH: The plugin-classification MSBuild property currently matches test projects too, which can copy test assemblies into plugin output directories.
- MEDIUM: Node Phase 22 and Python runtime evidence still have nondeterministic fallback paths that need targeted regression tests or deterministic replacement values.
Implementation Update - 2026-05-06
Implemented in docs-archive/implplan/SPRINT_20260506_004_Scanner_first_class_detector_corrections.md:
- Plugin packaging now excludes test/smoke/benchmark projects and copies copy-local dependency assemblies for restart plugins. Rust restart smoke now passes and no longer fails on missing
Tomlyn.dll. - Java now has
JavaAnalyzerPluginand a manifest pointing at the plugin entry point. PHP now has a manifest. - Secrets detection is explicitly packaged as a secrets analyzer plugin, but remains built into the Worker by default to avoid double-running. Worker option binding now maps enablement, scan limits, entropy, ruleset path, and verification settings into
SecretsAnalyzerOptions. - Secrets direct
ILanguageAnalyzerexecution now persists a typedSecretsAnalysisReport, ruleset version, andSecretContextSignalrecords inScanAnalysisStore. - Shared detector records were added for
LanguageDetectionFindingandSecretContextSignal, with stableScanAnalysisKeys.LanguageDetectionsandScanAnalysisKeys.SecretContextSignals. - Node Phase 22 pathless fallback component keys are content-derived instead of random. Python runtime events without source timestamps use a fixed sentinel timestamp.
- DotNet and Java deterministic-ordering warnings identified in this audit are fixed; both analyzer projects now build with zero warnings.
- Deno optional runtime capture no longer writes its shim or generated trace into the scan root. The runner writes to a scratch directory, returns trace bytes, and the analyzer ingests from memory.
LanguageAnalyzerSmokenow materializes Rust synthetic heuristic/fallback fixture binaries before restart smoke checks, so smoke coverage matches the analyzer tests.
Verification evidence from the implementation pass:
dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Secrets.Tests/StellaOps.Scanner.Analyzers.Secrets.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 199/199.dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Worker.Tests/StellaOps.Scanner.Worker.Tests.csproj" --filter "FullyQualifiedName~ScannerWorkerOptionsValidatorTests" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 158/158. The Microsoft Testing Platform runner executed the full project despite the filter.dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Node.Tests/StellaOps.Scanner.Analyzers.Lang.Node.Tests.csproj" --filter "FullyQualifiedName~NodePhase22SampleLoaderTests" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 370/370. The runner executed the full project despite the filter.dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Python.Tests/StellaOps.Scanner.Analyzers.Lang.Python.Tests.csproj" --filter "FullyQualifiedName~PythonRuntimeEvidenceCollectorTests" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 474/474. The runner executed the full project despite the filter.dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Deno.Tests/StellaOps.Scanner.Analyzers.Lang.Deno.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 31/31.dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.DotNet.Tests/StellaOps.Scanner.Analyzers.Lang.DotNet.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 181/181.dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Java.Tests/StellaOps.Scanner.Analyzers.Lang.Java.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 376/376.dotnet test "src/Tools/__Tests/LanguageAnalyzerSmoke.Tests/LanguageAnalyzerSmoke.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 4/4.dotnet run --no-build --project "src/Tools/LanguageAnalyzerSmoke/LanguageAnalyzerSmoke.csproj" -- --repo-root . --analyzer python-> PASS, scenariossimple-venv,pip-cache,layered-editable.dotnet run --no-build --project "src/Tools/LanguageAnalyzerSmoke/LanguageAnalyzerSmoke.csproj" -- --repo-root . --analyzer rust-> PASS, scenariossimple,heuristics,fallback.
Continuation implemented after the initial pass:
- Shared detector records now carry first-class SARIF/policy fields: rule ID, kind, severity, confidence, evidence locator, component key, entrypoint ID, reachability, suppressibility, tags, metadata, and deterministic fingerprints.
DetectorFindingFingerprintprovides deterministic fingerprints for language detections and secret context signals.DetectorFindingSarifMappermapsLanguageDetectionFindingandSecretContextSignalinto SARIF findings without analyzer-specific payload parsing.ScanAnalysisCompositionBuilder.BuildDetectorPolicyFindingsconsumes the shared analysis keys for policy-facing detector findings.- Ruby now has an active-path
AGENTS.md, and the optional Ruby runtime trace runner is hardened: explicit opt-in, entrypoint/root containment, allowlisted Ruby binary, bounded environment, timeout, scratch-directory shim/output, returned trace bytes, and no scan-root mutation.
Additional verification evidence from the continuation:
dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Core.Tests/StellaOps.Scanner.Core.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 341/341.dotnet test "src/Scanner/__Libraries/StellaOps.Scanner.Sarif.Tests/StellaOps.Scanner.Sarif.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 72/72.dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Emit.Tests/StellaOps.Scanner.Emit.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 222/222.dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Ruby.Tests/StellaOps.Scanner.Analyzers.Lang.Ruby.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 22/22.dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Worker.Tests/StellaOps.Scanner.Worker.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 158/158.dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Secrets.Tests/StellaOps.Scanner.Analyzers.Secrets.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 199/199.
Final uplift implemented in docs-archive/implplan/SPRINT_20260506_005_Scanner_language_detector_uplift_program.md:
LanguageDetectorFindingExtractornow merges component-derived detector evidence with bounded workspace source-pattern findings for Python, Node, Deno, Bun, Ruby, PHP, Rust, Go, DotNet, and Java.- Each language family emits shared first-class detector records for route, entrypoint, secret-context, and security-sink findings; Deno also emits remote-import records.
- Worker persistence stores the merged detector records under
ScanAnalysisKeys.LanguageDetectionseven when source-pattern findings are the only detector evidence. - Web policy/evidence API models preserve detector references, so UI-facing consumers can carry rule id, kind, fingerprint, evidence locator, policy predicate, language, and analyzer id without parsing analyzer-specific payloads.
- Ambiguous Bun/Deno JavaScript patterns were tightened so generic Express-style route lines are not promoted as Bun/Deno findings.
Final uplift verification evidence:
dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Tests/StellaOps.Scanner.Analyzers.Lang.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 157/157.dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Worker.Tests/StellaOps.Scanner.Worker.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 158/158.npx vitest run --config vitest.codex.config.ts src/app/core/api/policy-evidence.client.spec.tsfromsrc/Web/StellaOps.Web-> PASS, 2/2.dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Emit.Tests/StellaOps.Scanner.Emit.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 222/222.dotnet test "src/Scanner/__Libraries/StellaOps.Scanner.Sarif.Tests/StellaOps.Scanner.Sarif.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false-> PASS, 72/72.
Remaining work:
- Runtime timestamp paths that use injected
TimeProviderremain acceptable for replay when callers provide a fixed time; the old Python wall-clock fallback is closed. - The first-class detector baseline is complete end to end for shared contracts, Worker persistence, SARIF/policy/UI consumption, and all-language source-pattern coverage.
- Deeper AST/callgraph precision remains future analyzer-specific product hardening, not an open blocker in this audit.
Enhanced first-class detector audit:
- The original pass reviewed language analyzers mostly as package inventory emitters. The target state is stronger: every analyzer should be a first-class detector that emits typed security, semantic, reachability, and policy evidence.
- Secrets detection already exists as a separate Scanner analyzer/stage, but it was missing from this language analyzer audit. This document now includes it as a cross-cutting detector.
- The initial secrets finding was a wiring risk: the worker registered the secrets stage when
workerOptions.Secrets.Enabledwas true, butSecretsAnalyzerOptions.Enableddefaulted to false and the worker registration path did not set it. This was fixed inSPRINT_20260506_004.
First-Class Detector Standard
Treat an analyzer as first class only when it satisfies all of these gates:
Restart/deployment gate.
- Either it is a restart-loadable analyzer plugin with a manifest, copied dependency closure, and smoke profile, or it is explicitly documented as a built-in Worker stage with equivalent startup evidence.
- Test projects must never be copied as plugins.
Inventory gate.
- It emits deterministic SBOM components only from authoritative on-disk evidence, with PURL vs explicit-key decisions matching
language-analyzers-contract.md. - It records evidence locators, hashes when bounded, package scope, provenance, license, vendoring/bundling, and
usedByEntrypointwhere available.
- It emits deterministic SBOM components only from authoritative on-disk evidence, with PURL vs explicit-key decisions matching
Detector gate.
- It emits language-specific detections, not only package records: entrypoints, routes, framework identity, runtime surface, dynamic code, native/FFI, network, filesystem, process execution, database, queue, crypto/TLS, deserialization, template rendering, and secret/config access.
- Detections must be typed and queryable, not hidden only as ad hoc metadata strings.
Reachability gate.
- It joins inventory to entrypoints, dependency/call/import graphs, runtime evidence, and confidence.
- It can distinguish “present” from “used by runtime path” and “declared only” from “observed”.
Secrets gate.
- The cross-cutting secrets detector scans raw files and emits secret findings.
- Language analyzers should add context around secret use without duplicating raw-value detection: environment-variable names, config keys, dotenv loaders, framework secret stores, credentials file conventions, and secret sinks/sources.
- No analyzer may emit raw secret values in component metadata, logs, observations, SARIF, or policy payloads.
Policy/SARIF gate.
- Detector output must map to stable rule ids, severity, confidence, evidence locator, deterministic id/fingerprint, SARIF export, and policy predicates.
- Findings need suppressions/exception handling and baseline/diff semantics where false positives are expected.
Test gate.
- Each analyzer needs targeted
.csprojtests, fixture/golden outputs, negative cases, plugin smoke, packaging checks, and at least one fixture that proves secret/config context is masked and deterministic.
- Each analyzer needs targeted
Secrets Detection Audit
Current implementation:
src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Secrets/SecretsAnalyzer.cs:8implementsILanguageAnalyzerwith idsecretsand display nameSecret Leak Detector.SecretsAnalyzer.cs:62exposes a byte-content analysis path used by the Worker stage;SecretsAnalyzer.cs:96also exposes the normalILanguageAnalyzerpath.CompositeSecretDetector.cs:42routes regex, entropy, and composite rules;RegexDetector.cs:51uses rule keyword prefiltering;EntropyDetector.cs:11uses a high-entropy candidate regex.SecretsAnalyzerStageExecutor.cs:50registers the stage asscan-secrets;SecretsAnalyzerStageExecutor.cs:140stores aSecretsAnalysisReportinScanAnalysisKeys.SecretFindings.SecretFinding.cs:65andSecretFinding.cs:93derive deterministic finding ids from rule, file, line, column, and artifact digest.docs/modules/scanner/operations/secret-leak-detection.mddocuments rule bundles, masking, policy helpers, settings, alerts, and operational rollout.
Secrets test evidence:
dotnet test "src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Secrets.Tests/StellaOps.Scanner.Analyzers.Secrets.Tests.csproj" -v minimal /m:1 /p:UseSharedCompilation=false
Result: PASS, 198 passed, 0 failed, 0 skipped.
Secrets detector findings:
S1 - Worker enablement does not appear to enable the analyzer host
Severity: HIGH
Evidence:
src/Scanner/StellaOps.Scanner.Worker/Program.cs:273checksworkerOptions.Secrets.Enabled.Program.cs:275-279callsAddSecretsAnalyzer(...)and wires path/entropy settings, but does not setoptions.Enabled = true.src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Secrets/SecretsAnalyzerOptions.cs:19defaultsEnabledto false.SecretsAnalyzerHost.cs:52exits early whenoptions.Enabledis false.
Impact: enabling the Worker secrets stage can still leave SecretsAnalyzerHost disabled, so no ruleset is loaded and _secretsAnalyzer.AnalyzeAsync(...) returns no findings.
Follow-up:
- Set
SecretsAnalyzerOptions.Enabled = workerOptions.Secrets.Enabledin Worker registration, or bind Worker and analyzer options from one source. - Add a Worker integration test that starts with
Worker:Secrets:Enabled=true, verifies the host loads a fixture bundle, and then verifies a seeded secret producesScanAnalysisKeys.SecretFindings.
S2 - Bundle verification defaults are fail-open in analyzer options
Severity: HIGH
Evidence:
SecretsAnalyzerOptions.cs:91defaultsFailOnInvalidBundleto false.SecretsAnalyzerOptions.cs:96defaultsRequireSignatureVerificationto false.SecretsAnalyzerOptions.cs:117defaultsRequireRekorProofto false.SecretsAnalyzerHost.cs:169-177logs and continues when verification fails unless fail-on-invalid is enabled.- Core tenant settings default
RequireSignedRuleBundlesto true, but the Worker registration shown inProgram.cs:275-279does not map that setting into analyzer options.
Impact: production-looking deployments can run with unsigned or invalid rule bundles unless stricter configuration is explicitly bridged into the analyzer host.
Follow-up:
- Wire tenant/Worker secret detection settings into
FailOnInvalidBundle,RequireSignatureVerification, andRequireRekorProof. - Make production Worker startup fail closed when secrets are enabled and no valid signed bundle is available.
S3 - Worker scan limits and analyzer scan limits are split
Severity: MEDIUM
Evidence:
ScannerWorkerOptions.SecretsOptionsdefinesMaxFileSizeBytesandMaxFilesPerJob.Program.cs:275-279does not mapMaxFileSizeBytesintoSecretsAnalyzerOptions.SecretsAnalyzerStageExecutor.cs:169usesShouldScanFile, and the implementation hardcodes a 10 MB file limit rather than using Worker options.- The direct analyzer path uses
SecretsAnalyzerOptions.ExcludeDirectories; the Worker stage path uses file entries and extension/kind checks but does not apply the same directory exclusions.
Impact: scan behavior can differ between direct analyzer execution and Worker stage execution, causing noisy findings, unexpected cost, or missed large/small-file policy decisions.
Follow-up:
- Use a single
SecretScanFilePolicyfor both execution paths. - Wire
MaxFilesPerJob,MaxFileSizeBytes, extension filters, directory filters, and binary detection consistently.
S4 - Direct ILanguageAnalyzer path does not persist findings
Severity: MEDIUM
Evidence:
SecretsAnalyzer.cs:137callsStoreFindingsfor the direct analyzer path.SecretsAnalyzer.cs:266-273is a TODO that only logs “Would store” instead of writing toScanAnalysisStore.- The Worker stage path does persist a report at
SecretsAnalyzerStageExecutor.cs:140.
Impact: secrets detection works only through the Worker stage path. If SecretsAnalyzer is ever loaded as a normal language analyzer plugin, findings can disappear.
Follow-up:
- Either remove the direct
ILanguageAnalyzerregistration and document secrets as Worker-only, or implement the direct path againstScanAnalysisKeys.SecretFindings. - Add a test for direct analyzer execution with
LanguageAnalyzerContext.AnalysisStore.
S5 - Secrets detector is language-blind and lacks language-context enrichment
Severity: MEDIUM
Evidence:
- The detector scans bytes with regex/entropy rules and file path filters.
- Language analyzers detect environment/config behavior in several places, but they do not currently feed secret context into the secrets report.
Impact: the system can find raw leaked values, but it cannot yet say whether a finding is in a framework secret store, a runtime-only environment bridge, a build-time config, a test fixture, or an actually reachable entrypoint path.
Follow-up:
- Add a shared
SecretContextSignalor equivalentScanAnalysisKeyspayload with language, config key, framework, route/entrypoint reachability, package owner, and suppression hints. - Keep raw matching centralized in
StellaOps.Scanner.Analyzers.Secrets; use language analyzers to improve context and confidence.
S6 - Secrets detector has no manifest/plugin packaging story
Severity: LOW
Evidence:
src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Secretshas nomanifest.json.ServiceCollectionExtensions.cs:83-88registers the analyzer and host through DI, so it is currently a built-in service path rather than a restart plugin path.
Impact: this is acceptable if secrets stays built into Worker, but it should be explicit because language analyzers are moving toward restart plugin smoke gates.
Follow-up:
- Decide whether secrets detection is a built-in Worker stage or a restart plugin.
- If built-in, document that exception in the detector standard and add Worker startup smoke.
- If plugin, add a manifest and packaging smoke.
Inventory Matrix
| Area | Source project | Manifest | Plugin entry | Local AGENTS/TASKS | Tests and fixtures | Docs |
|---|---|---|---|---|---|---|
| Shared core | src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang | none expected | none expected | AGENTS yes, TASKS yes | Lang.Tests: 19 test files, 81 fixture files, 15 goldens | language-analyzers-contract.md |
| Bun | ...Lang.Bun | stellaops.analyzer.lang.bun; BunAnalyzerPlugin | BunAnalyzerPlugin : ILanguageAnalyzerPlugin | AGENTS yes, TASKS yes | Bun.Tests: 7 test files, 59 fixture files, 18 goldens | analyzers-bun.md |
| Deno | ...Lang.Deno | stellaops.analyzer.lang.deno; DenoAnalyzerPlugin | DenoAnalyzerPlugin : ILanguageAnalyzerPlugin | AGENTS missing in __Libraries, TASKS yes; legacy AGENTS exists at src/Scanner/StellaOps.Scanner.Analyzers.Lang.Deno | Deno.Tests: 22 test files, 12 fixture files, 0 goldens | design/deno-analyzer-plan.md, design/deno-analyzer-scope.md |
| DotNet | ...Lang.DotNet | stellaops.analyzer.lang.dotnet; DotNetAnalyzerPlugin | DotNetAnalyzerPlugin : ILanguageAnalyzerPlugin | AGENTS yes, TASKS yes | DotNet.Tests: 11 test files, 0 fixture files, 0 goldens | dotnet-analyzer.md |
| Go | ...Lang.Go | stellaops.analyzer.lang.go; GoAnalyzerPlugin | GoAnalyzerPlugin : ILanguageAnalyzerPlugin | AGENTS yes, TASKS yes | Go.Tests: 5 test files, 18 fixture files, 7 goldens | analyzers-go.md |
| Java | ...Lang.Java | stellaops.analyzer.lang.java; JavaLanguageAnalyzer | missing *AnalyzerPlugin.cs | AGENTS yes, TASKS yes | Java.Tests: 27 test files, 38 fixture files, 15 goldens | analyzers-java.md |
| Node | ...Lang.Node | stellaops.analyzer.lang.node; NodeAnalyzerPlugin | NodeAnalyzerPlugin : ILanguageAnalyzerPlugin | AGENTS yes, TASKS yes | Node.Tests: 16 test files, 58 fixture files, 17 goldens; Node.SmokeTests: 2 test files | analyzers-node.md |
| PHP | ...Lang.Php | missing | PhpAnalyzerPlugin : ILanguageAnalyzerPlugin | AGENTS missing in __Libraries, TASKS yes; legacy AGENTS exists at src/Scanner/StellaOps.Scanner.Analyzers.Lang.Php | Php.Tests: 9 test files, 14 fixture files, 7 goldens | php-analyzer-owner-manifest.md, design/php-autoload-design.md |
| Python | ...Lang.Python | stellaops.analyzer.lang.python; PythonAnalyzerPlugin | PythonAnalyzerPlugin : ILanguageAnalyzerPlugin | AGENTS yes, TASKS yes | Python.Tests: 21 test files, 102 fixture files, 14 goldens | analyzers-python.md |
| Ruby | ...Lang.Ruby | stellaops.analyzer.lang.ruby; RubyAnalyzerPlugin | RubyAnalyzerPlugin : ILanguageAnalyzerPlugin | AGENTS missing in __Libraries, TASKS yes; legacy AGENTS exists at src/Scanner/StellaOps.Scanner.Analyzers.Lang.Ruby | Ruby.Tests: 2 test files, 48 fixture files, 7 goldens | design/ruby-analyzer.md |
| Rust | ...Lang.Rust | stellaops.analyzer.lang.rust; RustAnalyzerPlugin | RustAnalyzerPlugin : ILanguageAnalyzerPlugin | AGENTS yes, TASKS yes; test project TASKS missing | Rust.Tests: 4 test files, 0 fixture files, 0 goldens | architecture.md Rust row |
Notes:
- Test project AGENTS files are absent across language analyzer tests. This is acceptable if the Scanner-level AGENTS is intended to bind all tests, but the missing
Rust.Tests/TASKS.mdis inconsistent with the rest of the test project layout. - Current manifests do not include a
statusfield.docs/modules/scanner/architecture.md:83says the Rust manifest status is production, so docs and manifests drift.
Command Evidence
Build sweep command:
$projects = @(
'src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang/StellaOps.Scanner.Analyzers.Lang.csproj',
'src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Bun/StellaOps.Scanner.Analyzers.Lang.Bun.csproj',
'src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Deno/StellaOps.Scanner.Analyzers.Lang.Deno.csproj',
'src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.DotNet/StellaOps.Scanner.Analyzers.Lang.DotNet.csproj',
'src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Go/StellaOps.Scanner.Analyzers.Lang.Go.csproj',
'src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Java/StellaOps.Scanner.Analyzers.Lang.Java.csproj',
'src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Node/StellaOps.Scanner.Analyzers.Lang.Node.csproj',
'src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Php/StellaOps.Scanner.Analyzers.Lang.Php.csproj',
'src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Python/StellaOps.Scanner.Analyzers.Lang.Python.csproj',
'src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Ruby/StellaOps.Scanner.Analyzers.Lang.Ruby.csproj',
'src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Rust/StellaOps.Scanner.Analyzers.Lang.Rust.csproj'
)
foreach ($project in $projects) {
dotnet build $project -v minimal /m:1 /p:UseSharedCompilation=false
}
Build results:
| Project | Result | Notes |
|---|---|---|
| Shared core | PASS | Dependency warnings only. |
| Bun | PASS | No analyzer-local warnings captured. |
| Deno | PASS | No analyzer-local warnings captured. |
| DotNet | PASS | Analyzer warning: MsBuildPropertyResolver.cs:122 STELLA0102 deterministic ordering. |
| Go | PASS | No analyzer-local warnings captured. |
| Java | PASS | Analyzer warnings: JavaPropertyResolver.cs:117 and JavaEntrypointResolver.cs:439 STELLA0102 deterministic ordering. |
| Node | PASS | No analyzer-local warnings captured. |
| PHP | PASS | No analyzer-local warnings captured. |
| Python | PASS | No analyzer-local warnings captured. |
| Ruby | PASS | No analyzer-local warnings captured. |
| Rust | PASS | No analyzer-local warnings captured. |
Test sweep command:
$projects = @(
'src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Tests/StellaOps.Scanner.Analyzers.Lang.Tests.csproj',
'src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Bun.Tests/StellaOps.Scanner.Analyzers.Lang.Bun.Tests.csproj',
'src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Deno.Tests/StellaOps.Scanner.Analyzers.Lang.Deno.Tests.csproj',
'src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.DotNet.Tests/StellaOps.Scanner.Analyzers.Lang.DotNet.Tests.csproj',
'src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Go.Tests/StellaOps.Scanner.Analyzers.Lang.Go.Tests.csproj',
'src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Java.Tests/StellaOps.Scanner.Analyzers.Lang.Java.Tests.csproj',
'src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Node.Tests/StellaOps.Scanner.Analyzers.Lang.Node.Tests.csproj',
'src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Node.SmokeTests/StellaOps.Scanner.Analyzers.Lang.Node.SmokeTests.csproj',
'src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Php.Tests/StellaOps.Scanner.Analyzers.Lang.Php.Tests.csproj',
'src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Python.Tests/StellaOps.Scanner.Analyzers.Lang.Python.Tests.csproj',
'src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Ruby.Tests/StellaOps.Scanner.Analyzers.Lang.Ruby.Tests.csproj',
'src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Rust.Tests/StellaOps.Scanner.Analyzers.Lang.Rust.Tests.csproj'
)
foreach ($project in $projects) {
dotnet test $project -v minimal /m:1 /p:UseSharedCompilation=false
}
Targeted test results:
| Test project | Result |
|---|---|
Lang.Tests | PASS: 154 passed, 0 failed, 0 skipped |
Bun.Tests | PASS: 115 passed, 0 failed, 0 skipped |
Deno.Tests | PASS: 31 passed, 0 failed, 0 skipped |
DotNet.Tests | PASS: 181 passed, 0 failed, 0 skipped |
Go.Tests | PASS: 99 passed, 0 failed, 0 skipped |
Java.Tests | PASS: 376 passed, 0 failed, 0 skipped |
Node.Tests | PASS: 369 passed, 0 failed, 0 skipped |
Node.SmokeTests | PASS: 1 passed, 0 failed, 0 skipped |
Php.Tests | PASS: 250 passed, 0 failed, 0 skipped |
Python.Tests | PASS: 473 passed, 0 failed, 0 skipped |
Ruby.Tests | PASS: 18 passed, 0 failed, 0 skipped |
Rust.Tests | PASS: 15 passed, 0 failed, 0 skipped |
No-build rerun command used to capture counts that were truncated in the first sweep:
dotnet test <individual-test-project.csproj> --no-build --no-restore -v minimal /m:1 /p:UseSharedCompilation=false
Smoke harness commands:
dotnet run --project src/Tools/LanguageAnalyzerSmoke/LanguageAnalyzerSmoke.csproj -- --repo-root . --analyzer python
dotnet run --project src/Tools/LanguageAnalyzerSmoke/LanguageAnalyzerSmoke.csproj -- --repo-root . --analyzer rust
Smoke results:
| Analyzer | Result | Evidence |
|---|---|---|
| Python | PASS | Loaded analyzer python; scenarios simple-venv, pip-cache, and layered-editable passed cold/warm determinism and golden comparison. |
| Rust | FAIL | Loaded analyzer rust, then failed: Could not load file or assembly 'Tomlyn, Version=0.19.0.0, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified. Plugin directory contained manifest.json, StellaOps.Scanner.Analyzers.Lang.Rust.dll, and pdb, but not Tomlyn.dll. |
Findings
F1 - Restart plugin packaging misses copy-local dependencies
Severity: HIGH
Evidence:
src/Directory.Build.targets:86definesScannerCopyLangAnalyzerPluginArtifacts.src/Directory.Build.targets:94-98copies only$(TargetPath), optional$(TargetPath).deps.json, pdb, andmanifest.json.src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Rust/StellaOps.Scanner.Analyzers.Lang.Rust.csproj:40referencesTomlyn.- Rust smoke failed after loading the plugin with a missing
Tomlynassembly.
Impact: analyzers can pass unit tests and normal source builds but fail when deployed as restart plugins. This directly affects offline kit promises and Worker plugin loadability.
Follow-up:
- Update the packaging target to copy
ReferenceCopyLocalPathsor publish plugin directories through a deterministic plugin publish target. - Add a packaging test or smoke profile that asserts dependency assemblies are present for package-dependent analyzers.
- Rerun Rust smoke after the packaging fix.
F2 - Plugin packaging rule also matches test projects
Severity: HIGH
Evidence:
src/Directory.Build.props:149setsIsScannerLangAnalyzerPlugin=truefor any project whose MSBuild project name starts withStellaOps.Scanner.Analyzers.Lang..- Test projects such as
StellaOps.Scanner.Analyzers.Lang.Rust.Testsmatch that prefix.
Impact: building tests can copy test assemblies into plugins/scanner/analyzers/lang/<test-project>. That pollutes local plugin outputs and can hide release packaging mistakes.
Follow-up:
- Exclude
*.Testsand*.SmokeTestsprojects fromIsScannerLangAnalyzerPlugin. - Consider requiring an explicit property in production analyzer projects rather than inferring from name.
F3 - Java manifest is not compatible with the shared plugin catalog
Severity: HIGH
Evidence:
src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Java/manifest.json:10declares entry typeStellaOps.Scanner.Analyzers.Lang.Java.JavaLanguageAnalyzer.- Java has no
*AnalyzerPlugin.cs. - The shared catalog loads
ILanguageAnalyzerPluginimplementations (LanguageAnalyzerPluginCatalog.cs:131) and creates analyzers throughCreateAnalyzers(LanguageAnalyzerPluginCatalog.cs:83).
Impact: Java has a manifest but is unlikely to restart-load through the same plugin catalog used by the other language analyzers. Static/in-process tests still pass, so this needs a plugin-load smoke rather than more analyzer unit tests.
Follow-up:
- Add
JavaAnalyzerPlugin : ILanguageAnalyzerPluginand update the manifest entry point, or explicitly document and test a separate Java loading path. - Add Java to
LanguageAnalyzerSmokeprofiles.
F4 - PHP plugin has no manifest
Severity: HIGH
Evidence:
src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Php/PhpAnalyzerPlugin.cs:5implementsILanguageAnalyzerPlugin.- No
src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Php/manifest.jsonexists. docs/OFFLINE_KIT.md:32says the kit carries the PHP language analyzer plugin directory with manifests.
Impact: PHP cannot be copied as a complete restart plugin by the current manifest-based target and offline docs overstate current artifact reality.
Follow-up:
- Add PHP
manifest.jsonwith restart-only semantics andPhpAnalyzerPluginentry point. - Add PHP to
LanguageAnalyzerSmokeprofiles. - Keep offline-kit docs aligned with the real release artifact list.
F5 - Node Phase 22 has nondeterministic explicit-key fallbacks
Severity: HIGH
Evidence:
src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Node/Internal/Phase22/NodePhase22Exporter.cs:91falls back toGuid.NewGuid().ToString("N")for a missing component path.src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Node/Internal/Phase22/NodePhase22Exporter.cs:166has the same fallback for entrypoint records.docs/modules/scanner/architecture.md:81says a prior NodeGuid.NewGuid()runtime fallback was closed with deterministic tuple hashing.
Impact: identical observation input with a missing Path can produce different SBOM output between cold and warm runs. Existing tests appear to cover normal path-bearing observations, not the missing-path branch.
Follow-up:
- Replace the fallback with a deterministic key derived from stable observation fields and evidence hash.
- Add a regression test for pathless Phase 22 component and entrypoint records.
F6 - Python runtime evidence timestamp fallback is wall-clock based
Severity: MEDIUM
Evidence:
PythonRuntimeEvidenceCollector.cs:159,:185,:234,:269,:309,:335, and:369callGetUtcTimestamp()when runtime event timestamps are absent.PythonRuntimeEvidenceCollector.cs:396returnsDateTime.UtcNow.
Impact: static Python analysis remains covered, but optional runtime evidence with missing timestamps can differ between identical input runs.
Follow-up:
- Prefer a deterministic missing-timestamp marker or inject a fixed time source through the runtime evidence path.
- Add a regression test for runtime event records without timestamps.
F7 - Analyzer build still emits deterministic-ordering warnings
Severity: MEDIUM
Evidence:
- DotNet build warning:
MsBuildPropertyResolver.cs:122STELLA0102 on_propertyChain. - Java build warnings:
JavaPropertyResolver.cs:117andJavaEntrypointResolver.cs:439STELLA0102.
Impact: builds pass, but deterministic analyzer warnings remain in language analyzer code. These should not be treated as release-clean.
Follow-up:
- Sort or freeze the iterated collections.
- Add narrow regression tests if ordering affects emitted metadata, edges, or component keys.
F8 - Smoke harness covers only Python and Rust
Severity: MEDIUM
Evidence:
LanguageAnalyzerSmokeRunner.cs:48-68defines profiles only forpythonandrust.- Smoke coverage is absent for Bun, Deno, DotNet, Go, Java, Node, PHP, and Ruby.
Impact: several analyzers have manifests and unit tests but no restart-plugin load and cold/warm determinism gate.
Follow-up:
- Add smoke profiles incrementally, starting with Java, PHP, and every analyzer with package dependencies.
- Make the smoke output part of offline kit release evidence.
F9 - Deno writes a runtime shim into the analyzed root
Severity: MEDIUM
Evidence:
DenoLanguageAnalyzer.cs:23callsTryWriteRuntimeShimAsyncbefore static collection.DenoLanguageAnalyzer.cs:84writesDenoRuntimeShimundercontext.RootPath.DenoRuntimeTraceRunner.cs:155-199does enforce binary allowlisting and root containment when runtime execution is requested.
Impact: Deno runtime execution is guarded, but static analysis can still mutate the analyzed root by writing the shim. This may be acceptable by current design, but it should be an explicit contract.
Follow-up:
- Decide whether language analyzers may write helper files under scan roots.
- If not, move runtime shims to analyzer-owned temp/output storage and add a no-root-mutation test.
F10 - Local ownership docs drift for Deno, PHP, Ruby, and Rust tests
Severity: LOW
Evidence:
- Current
__LibrariesDeno, PHP, and Ruby analyzer directories lackAGENTS.md. - Legacy root-level directories under
src/Scanner/StellaOps.Scanner.Analyzers.Lang.{Deno,Php,Ruby}still carry AGENTS files. src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Rust.TestslacksTASKS.md, unlike the other language analyzer test projects.
Impact: implementers may read stale local instructions or miss module-local workflow expectations.
Follow-up:
- Move or recreate Deno/PHP/Ruby AGENTS guidance in the active
__Librariesdirectories. - Add
Rust.Tests/TASKS.mdor document why Rust test tracking intentionally differs.
F11 - Offline-kit language analyzer list is out of sync with current source
Severity: LOW
Evidence:
docs/OFFLINE_KIT.md:21and:32list Node, Go, DotNet, Python, Ruby, Rust, and PHP plugin directories.- Bun, Deno, and Java source projects have manifests, but are absent from the offline-kit language analyzer list.
- PHP is listed, but lacks a source manifest.
Impact: release operators cannot tell from docs which language analyzers are expected to ship in the offline kit.
Follow-up:
- Align offline-kit docs with actual shipped plugin artifacts.
- Add release checks that compare source manifests, plugin output directories, and offline-kit manifest contents.
F12 - Ruby runtime trace runner is not currently wired, but needs hardening before use
Severity: LOW
Evidence:
RubyLanguageAnalyzer.cs:40collects runtime evidence from existing files.RubyRuntimeTraceRunner.cs:47readsSTELLA_RUBY_BINARY, butRubyLanguageAnalyzerdoes not currently callRubyRuntimeTraceRunner.
Impact: no current runtime-execution issue was found in Ruby analyzer flow, but the unused runner should not be wired later without root-containment, binary allowlisting, timeout, and tests comparable to Deno.
Follow-up:
- Before enabling Ruby runtime execution, add Deno-style tests for entrypoint containment, binary allowlisting, missing binary, timeout, and no-shim-on-skip behavior.
Analyzer Notes
Shared Core
The core build and Lang.Tests passed. The contract doc is explicit about PURL vs explicit-key identity, evidence locators, traversal bounds, and deterministic behavior. The plugin catalog loads ILanguageAnalyzerPlugin from copied plugin directories, so manifest-only correctness is not enough; each restart analyzer needs a compatible plugin implementation and deployment-ready dependency closure.
Bun
Bun has a manifest and plugin entry, targeted tests passed, and fixture/golden coverage is comparatively strong. No analyzer-local build warnings were captured. Remaining risk is cross-cutting: smoke coverage is absent and the MSBuild packaging rule can misclassify tests.
Deno
Deno has a manifest and plugin entry, targeted tests passed, and the runtime runner validates entrypoint containment, binary allowlisting, and root-contained binaries. The review found a root-mutation question: the analyzer writes the runtime shim to context.RootPath even before static collection. Deno also needs current-directory AGENTS hygiene under __Libraries.
DotNet
DotNet has a manifest and plugin entry, targeted tests passed, and the main issue is deterministic-ordering warning STELLA0102 in MsBuildPropertyResolver.cs:122. Fixture/golden inventory is lighter than Java/Node/Python, so parity work should favor behavioral edge cases around property inheritance and lock/deps resolution.
Go
Go has a manifest and plugin entry, targeted tests passed, and no analyzer-local build warnings were captured. It has fixture/golden coverage and should be a good early smoke-profile candidate because offline-kit docs already promise Go plugin artifacts.
Java
Java has strong tests and fixture/golden coverage, but restart plugin loadability is the largest issue. Its manifest points directly to JavaLanguageAnalyzer, while the shared catalog expects ILanguageAnalyzerPlugin. Java also has deterministic-ordering warnings in property resolution and SPI edge resolution.
Node
Node targeted tests and the existing test-project smoke passed. The main issue is the Phase 22 Guid.NewGuid() fallback for pathless component and entrypoint records, which reintroduces a deterministic-output risk similar to the runtime fallback that architecture says was already closed.
PHP
PHP targeted tests passed and it has an analyzer plugin class, but no manifest. This conflicts with the offline-kit docs that say PHP ships with a manifest-bearing plugin directory. PHP should get a manifest and smoke profile before release evidence claims plugin deployment parity.
Python
Python targeted tests passed and the language analyzer smoke passed across simple-venv, pip-cache, and layered-editable scenarios. The remaining deterministic gap is optional runtime evidence that replaces absent event timestamps with wall-clock UTC.
Ruby
Ruby targeted tests passed, with good fixture/golden inventory but only two test source files. The active analyzer reads existing runtime evidence; the unused runtime trace runner should be hardened before it is wired. Ruby also needs current-directory AGENTS hygiene under __Libraries.
Rust
Rust targeted tests passed, but smoke failed because the plugin deployment directory did not include the Tomlyn dependency. This is the clearest evidence that test pass counts do not prove restart plugin deployability. Rust test project TASKS hygiene is also inconsistent with the rest of the language analyzer tests.
Per-Analyzer First-Class Detector Gap Report
Shared Language Analyzer Core
Current strengths:
- Provides common analyzer contracts, context, usage hints, plugin catalog, and surface-secret access wrapper.
LanguageAnalyzerContextexposesUsageHints,AnalysisStore, service lookup, andLanguageAnalyzerSecrets.
Missing to be first class:
- No common typed detector-result model for language findings. Most analyzer-specific detections are emitted as component metadata or observation payloads.
- No common severity/confidence/rule-id contract for language detector output.
- No common bridge from language context into SARIF, policy predicates, UI findings, or secret-context enrichment.
Improvement plan:
- Add a shared
LanguageDetectionFindingmodel withruleId,kind,severity,confidence,evidence,componentKey,entrypointId,reachability, and deterministic fingerprint. - Add common helpers for route, capability, crypto, native/FFI, dynamic-code, deserialization, and secret-context detections.
- Add a
ScanAnalysisKeys.LanguageDetectionsaggregate so Policy/UI can consume all analyzers uniformly.
Bun
Current strengths:
- Handles Bun lock/install/declared dependency inventory, scope metadata, patches, custom registries, and workspace normalization.
- Has a manifest and plugin entry.
Missing to be first class:
- No Bun-specific capability scanner comparable to Node/Deno.
usedByEntrypointis always false in current component emission.- No Bun runtime/framework detection for
Bun.serve, Hono, Elysia, Express-on-Bun, WebSocket,Bun.spawn,Bun.file,Bun.env, SQLite, or test/build scripts. - No language-context contribution to secrets findings for
.env,bunfig.toml,process.env,Bun.env,.npmrc, registry tokens, or private package scopes. - No smoke profile.
Improvement plan:
- Reuse and extend Node/TypeScript scanning primitives for Bun APIs.
- Emit typed capability and route detections for Bun runtime APIs and frameworks.
- Mark package components reachable from scripts/entrypoints where Bun entrypoints are known.
- Add Bun smoke fixtures with
Bun.serve,.env, private registry token config, and patched dependency cases.
Deno
Current strengths:
- Builds module graph, import map, npm compatibility, remote-module inventory, bundle observations, and runtime trace payloads.
- Emits Deno capability records from builtin imports and remote fetch/import signals.
- Has guarded runtime runner tests for entrypoint containment and binary allowlisting.
Missing to be first class:
- No plugin smoke profile.
- Root mutation remains unresolved because the analyzer writes a runtime shim under
context.RootPath. - Capability detection is import/specifier based; it does not yet fully model Deno permissions,
deno.jsontask flags,--allow-*, unstable features, or runtime permission prompts. - No first-class route/framework detection for
Deno.serve, Fresh, Oak, Hono, Aleph, WebSocket, KV, cron, queues, or deploy/serverless patterns. - No Deno-specific secret-context signals for
Deno.env.get, dotenv modules, import maps pointing to private registries, npm auth, or permission-scoped environment access.
Improvement plan:
- Move runtime shim handling out of the scan root or explicitly contract root mutation.
- Add permission-model detector output with
allow-net,allow-env,allow-read,allow-run,allow-ffi, and exact source evidence. - Add route/framework detector and runtime reachability join for Deno entrypoints and bundle entrypoints.
- Add Deno smoke fixtures for permissions, remote imports, private npm registry, and secret env reads.
DotNet
Current strengths:
- Covers NuGet/deps/lock/project metadata, entrypoint resolver, runtime evidence loader, bundling/single-file/ILMerge signals, capabilities, crypto extraction, callgraph, licensing, Authenticode, and version conflicts.
- Has a manifest and plugin entry.
Missing to be first class:
- Deterministic-ordering warning remains in MSBuild property resolution.
- ASP.NET Core route detection is not first-class: minimal APIs, controllers, Razor Pages, SignalR hubs, gRPC services, hosted workers, Azure Functions, and Orleans actors need typed route/entrypoint output.
- Secret-context detection is not first-class for
appsettings*.json,UserSecretsId, environment variables, connection strings, KeyVault references, Kubernetes secret mounts,.pubxml, NuGet credentials, anddotnet user-secrets. - Security detectors are incomplete for unsafe deserialization, reflection/dynamic load, process execution sinks, SQL command construction, SSRF through
HttpClient, insecure TLS/certificate callbacks, and weak auth middleware. - Plugin smoke coverage is absent.
Improvement plan:
- Add route and framework detector over project files, source, compiled metadata, and endpoint conventions.
- Add config/secret-context detector with masking and handoff to the central secrets report.
- Emit typed findings for insecure crypto/TLS/deserialization/process/SQL/SSRF patterns, tied to component and entrypoint evidence.
- Add DotNet smoke fixture with ASP.NET endpoints, appsettings secrets, connection strings, and single-file deployment.
Go
Current strengths:
- Handles buildinfo in binaries, Go source inventory,
go.mod,go.sum,go.work, vendor data, CGO, private modules, capabilities, licenses, and version conflicts. - Has a manifest and plugin entry.
Missing to be first class:
- No structured route/framework detector for
net/http,chi,gin,echo,fiber,grpc, Cobra CLI, Lambda, or worker processes. - No Go callgraph/reachability join from main/package init/handler functions to dependencies and risky sinks.
- Secret-context detection is missing for
os.Getenv,viper,.env,go:embedconfig, Docker/Kubernetes env conventions, and private module credentials. - Security detectors should cover pprof exposure,
exec.Command, file writes, SSRF viahttp.Client, insecure TLS configs, weak crypto, SQL string construction, unsafe/cgo, plugins, reflection, andencoding/gob/YAML decode risks. - Plugin smoke coverage is absent.
Improvement plan:
- Add route/handler detector that emits HTTP/gRPC/CLI entrypoints.
- Add import/call sink detector for process, network, filesystem, SQL, TLS, crypto, and unsafe/cgo.
- Add secret-context signal integration for env/config libraries and private module auth.
- Add smoke fixture covering binary buildinfo, source-only module, route detection, secret env read, and cgo/native linkage.
Java
Current strengths:
- Handles Maven/Gradle metadata, lock data, classpath, OSGi, shaded/fat archives, embedded jars, runtime images, JNI, reflection, service-loader/SPI, callgraph, crypto, licenses, framework config hints, and version conflicts.
Missing to be first class:
- Java is not restart-loadable through the shared plugin catalog because it has a manifest but no
ILanguageAnalyzerPluginimplementation. - Deterministic-ordering warnings remain in property resolution and SPI edge resolution.
- Framework route detection is not first-class for Spring MVC/WebFlux, Spring Security, Quarkus, Micronaut, JAX-RS, Servlet filters, Kafka Streams, Camel, gRPC, and serverless handlers.
- Secret-context detection is missing for
application.yml,bootstrap.yml, Spring config imports, JNDI, keystores, truststores, Maven/Gradle credentials, environment/property reads, and vault/config-client libraries. - Security detectors should cover unsafe deserialization, JNDI lookup risk, XXE, expression language injection, SSRF, process execution, SQL construction, weak TLS, and reflective/dynamic class loading as typed findings.
Improvement plan:
- Add
JavaAnalyzerPluginand Java smoke profile first. - Add framework route/security detector that reads annotations, config files, and classpath metadata without executing code.
- Add config-secret detector with central secret finding enrichment and keystore/truststore evidence.
- Convert JNI/reflection/service-loader/crypto facts into typed findings with severity/confidence and policy rule ids.
Node
Current strengths:
- Handles npm/Yarn/pnpm-style inventory, runtime evidence, Phase 22 observations, environment warnings, lock integrity, scope metadata, and plugin loadability.
- Existing tests include a Node smoke test project.
Missing to be first class:
- Phase 22 still has nondeterministic
Guid.NewGuid()fallback paths for missing observation paths. - Route/framework detection is not complete enough as typed detector output for Express, NestJS, Fastify, Koa, Hapi, Next.js, Remix, serverless handlers, workers, WebSocket, and CLI bins.
- Secret-context detection needs stronger signals for
process.env, dotenv,.npmrc,.yarnrc,.pnpmfile, private registry tokens, cloud SDK credentials, JWT secrets, and config libraries. - Security detectors should cover dynamic
eval/Function, child process, prototype pollution packages/usages, SSRF sinks, template injection, insecure JWT verification, TLS bypass, path traversal, SQL/NoSQL injection patterns, and native addons. - Plugin smoke coverage should verify restart plugin load plus cold/warm determinism through the shared smoke harness, not only the test project.
Improvement plan:
- Fix Phase 22 deterministic keys and add pathless observation tests.
- Promote framework/route/env findings into typed language detections.
- Add secret-context handoff for env/config files and package-manager credential files.
- Add Node smoke fixture with routes, env secret reads, private registry config, and native addon evidence.
PHP
Current strengths:
- Handles Composer lock/package inventory, autoload graph, include graph, capability scanning, framework surface, environment settings, FFI, PHAR content, runtime evidence files, and version conflicts.
- Has a plugin class and strong targeted tests.
Missing to be first class:
- No
manifest.json, so it is not packaged consistently as a restart language analyzer. - Framework surface needs typed route and middleware output for Laravel, Symfony, Slim, WordPress, Drupal, Magento, Yii, CakePHP, and queue/CLI commands.
- Secret-context detection is missing for
.env,config/*.php,wp-config.php,parameters.yml,APP_KEY, database credentials, Composer auth, and framework vault/config conventions. - Security detectors should cover unsafe deserialization, PHAR gadget exposure,
eval, dynamic includes, shell execution, SSRF/file wrappers, SQL construction, weak crypto, insecure session/cookie config, and upload handling. - Runtime shim/evidence path needs the same hardening standard as Deno before any execution is enabled.
Improvement plan:
- Add PHP manifest and smoke profile.
- Promote framework surface and PHAR/FFI/include capabilities into typed findings.
- Add config/secret detector that contributes masked context to the central secrets report.
- Add PHP fixtures for Laravel/Symfony/WordPress routes,
.env, Composer auth, PHAR, dynamic include, and FFI.
Python
Current strengths:
- Handles lock/package discovery, VFS, container overlays, vendoring, entrypoint discovery, framework hints, imports, capabilities/native extensions, environment, startup hooks, runtime evidence, observation payloads, licenses, and Python smoke.
Missing to be first class:
- Optional runtime timestamp fallback remains wall-clock based when runtime events omit timestamps.
- Route detection needs typed output for Django URLConf, Flask/FastAPI decorators, Starlette, Celery tasks, Click/Typer commands, Lambda/Azure/Cloud Functions, notebooks/jobs, and ASGI/WSGI server invocations.
- Secret-context detection is missing for
os.environ, Pydantic settings, Django settings, Flask config, dotenv, cloud SDK credentials,.pypirc, pip config, Poetry/Pipenv auth, and Kubernetes secret mounts. - Security detectors should cover pickle/YAML unsafe loads,
eval/exec, subprocess, Jinja2 template injection, SSRF sinks, SQL construction, weak crypto/TLS, dependency confusion/private index config, native extension loading, and model artifact loading. - Python smoke covers package inventory but not typed route/security/secret detector output.
Improvement plan:
- Replace runtime timestamp fallback or inject fixed time for replay.
- Add route/task detector with framework-specific parsers and deterministic evidence.
- Add secret-context detector and central secrets enrichment for settings/config files.
- Add security sink detector fixtures and SARIF/policy rule ids for unsafe deserialization, dynamic execution, subprocess, SSRF, SQL, and weak crypto.
Ruby
Current strengths:
- Handles Gemfile/Gemfile.lock, Bundler config, container/vendor artifacts, runtime graph/evidence files, capabilities, observations, policy signal emission, and native extension metadata.
Missing to be first class:
- Active
__LibrariesAGENTS file is missing. - Ruby runtime trace runner is not currently wired and lacks the Deno-level hardening contract if enabled later.
- Route/framework detection is missing for Rails routes/controllers, Rack, Sinatra, Hanami, Sidekiq, Rake tasks, CLI gems, and serverless handlers.
- Secret-context detection is missing for Rails credentials/master.key,
.env,database.yml,secrets.yml, Bundler credentials, gem sources, cloud SDK config, and environment access. - Security detectors should cover unsafe YAML/Marshal deserialization,
eval/send/dynamic constantize, shell execution, SSRF/open-uri, SQL construction, weak crypto/TLS, template injection, and native extension loading.
Improvement plan:
- Add current-path AGENTS file and either harden or keep runtime runner explicitly unwired.
- Add Rails/Rack route detector and Sidekiq/Rake/CLI entrypoint detector.
- Add secret-context enrichment for Rails/Bundler config and environment reads.
- Add typed security findings and Ruby smoke profile with Rails route, credentials, and native extension fixtures.
Rust
Current strengths:
- Handles Cargo.lock, Cargo.toml, workspace members, declared-vs-installed merge, binary fingerprints, license metadata, and
usedByEntrypointfrom usage hints. - Has a manifest and plugin entry, but smoke currently fails because dependency assemblies are not copied.
Missing to be first class:
- Restart plugin packaging is incomplete for Tomlyn dependency.
- No Rust capability scanner for unsafe blocks, FFI, process execution, filesystem, network, database, crypto/TLS, environment access, dynamic loading, proc macros, build scripts, or feature-gated code.
- No route/framework detector for Axum, Actix Web, Rocket, Warp, Tonic/gRPC, Tower, Lambda, Tokio workers, Clap CLI, or background services.
- Secret-context detection is missing for
std::env, dotenv crates, config crates,.cargo/credentials, private registries, build.rs environment access, and embedded secrets throughinclude_str!/include_bytes!. - Security detectors should cover unsafe/FFI boundary, serde/bincode/postcard deserialization, command execution, SSRF, SQL construction, weak crypto/TLS, path traversal, feature confusion, yanked/advisory metadata, and supply-chain risk from build scripts/proc macros.
Improvement plan:
- Fix plugin dependency copying and rerun Rust smoke.
- Add source scanner using a conservative Rust syntax/token model for
unsafe, env/config, build.rs, proc macro, network, process, SQL, crypto, and deserialization indicators. - Add framework route detector for common web/gRPC/CLI frameworks.
- Add secret-context enrichment for Cargo credentials, private registries, dotenv/config crates, and embedded file macros.
Cross-Analyzer Backlog
Recommended follow-up sprint candidates:
Scanner language analyzer plugin packaging hardening.
- Fix copy-local dependency inclusion.
- Exclude test projects from plugin packaging.
- Add packaging assertions for dependency-bearing analyzers.
- Close Rust smoke failure.
Restart plugin loadability parity.
- Add Java
ILanguageAnalyzerPluginor document/test a separate loader. - Add PHP manifest.
- Expand smoke profiles for Java, PHP, Go, Node, Ruby, DotNet, Bun, and Deno.
- Add Java
Determinism cleanup.
- Replace Node Phase 22
Guid.NewGuid()fallbacks. - Replace or inject Python runtime timestamp fallback.
- Close DotNet/Java STELLA0102 warnings with tests where output ordering changes.
- Replace Node Phase 22
Scanner analyzer docs and ownership hygiene.
- Align offline-kit analyzer lists with real source manifests and plugin outputs.
- Move or recreate Deno/PHP/Ruby AGENTS files under active
__Librariespaths. - Add or explicitly waive
Rust.Tests/TASKS.md.
Runtime execution contract review.
- Decide whether analyzers may write runtime shims into scan roots.
- Before wiring Ruby runtime execution, add Deno-equivalent containment, allowlist, timeout, and skip-behavior tests.
First-class detector framework.
- Add shared typed detector output with rule ids, severity, confidence, evidence locators, deterministic fingerprints, and SARIF/policy mapping.
- Add
ScanAnalysisKeys.LanguageDetectionsand exporters for Policy, SARIF, UI, and evidence bundles. - Convert ad hoc capability/framework/crypto/runtime metadata into typed findings without losing SBOM component metadata.
Secrets detector first-class integration.
- Fix Worker/analyzer enablement mismatch.
- Wire fail-closed signed bundle settings into the Worker path.
- Unify file-size, file-count, extension, directory, and binary filters.
- Decide whether secrets is Worker-built-in or restart plugin, and add the matching smoke/startup evidence.
- Add language secret-context signals from each analyzer into the central secrets report.
Per-language security detector expansion.
- Add route/framework detectors for Bun, Deno, DotNet, Go, Java, Node, PHP, Python, Ruby, and Rust.
- Add typed security findings for dynamic code, process execution, SSRF, SQL/NoSQL injection sinks, deserialization, weak crypto/TLS, native/FFI, template injection, and config/secret access.
- Add one language-specific secret/config fixture per analyzer and one negative fixture proving masking/suppression behavior.
