0011 - Ecosystem Collector
Context¶
Ecosystem detection logic (CI platforms, deployment tools, application
frameworks) was scattered across 4+ resolvers in private keyword tables.
Each resolver re-walked the file list independently, and CI platform
detection was duplicated between verifiability_ci.go and
aggregation_secrets.go.
Decision¶
-
Create
internal/collector/ecosystem/with typed detection rules for CI platforms (9), deployment tools (14), and application frameworks (2). Detection runs once per scan in a single pass over the file list. -
Add
EcosystemFactstype tomodelwithHas(name),HasCI(),CIFiles(), andHasDeployment()query methods. -
Add
Ecosystems() EcosystemFactstomodel.FactStoreinterface. This is a public interface change (same category as ADR 0010). -
Migrate resolvers:
verifiability_ci.go: replaced privateciConfigFiles+ciConfigDirPrefixeswithfacts.Ecosystems().HasCI()aggregation_secrets.go: replaced duplicate CI detection withfacts.Ecosystems().CIFiles()-
explicitness.go: usesfacts.Ecosystems().Has("spring")andHas("rails")as fast-path skip before file walks -
reversibility.goretains its privatehasDeploymentArtifactsfunction for now — the deployment artifact list includes directory prefixes (deploy/,deployment/) that don't map 1:1 to ecosystem names. Full migration deferred to avoid scope creep.
Consequences¶
- Single round of ecosystem detection per scan (was 2-4 independent walks).
- Adding a new CI platform or deployment tool is a one-line change in the collector, not a hunt through multiple resolvers.
- All
FactStoreimplementations must addEcosystems(). - The
secretScannerKeywordslist inaggregation_secrets.goremains resolver-private — it's tool-specific, not ecosystem detection.