engine: detection, which is what makes af init worth running
internal/detect derives a manifest draft from a repository by reading files. Twelve analyzers cover Node and its frameworks, Python with Django, FastAPI, Flask, and Celery, Go, Rails and Sidekiq, Dockerfiles, Compose, Procfiles, migration tools, environment variables, third party SDKs, and schedules. Two constraints shape the whole package. The repository is untrusted input, so detection never executes anything from it. No npm install, no go run, no evaluating a config file that happens to be JavaScript. Everything is parsing, every parser is bounded, symlinks are not followed, and the walk has limits on file size, total bytes, file count, and wall time. A budget that runs out returns partial results with an explicit note, because an af init that appears to hang is worse than one that says what it did not finish. Detection is deterministic. af init writes a manifest that gets committed, and one that shuffles on every run is unusable in review, so findings are sorted before merging and a test runs the same tree ten times and compares. The merger's rule: high confidence goes into the draft silently, anything less becomes a question, and a disagreement between analyzers always becomes a question, because a conflict is exactly where a silent choice is most likely to be wrong. Every finding names the file that produced it, so a user can check the reasoning rather than trust it. The third party catalog is the part a user could not write themselves. It maps an SDK to its hostnames and to a default egress mode, and the modes are judgements stated in the manifest as notes: Stripe gets sandbox because it has a real one, mail providers get capture because "allow" means a preview environment emails a real customer, analytics and error reporting get block because they would pollute production reporting, and model providers get mock because a preview run should cost nothing and answer the same way twice. Compose parsing reads the container side of a port mapping rather than the host side, since "8080:3000" means the application binds 3000, and reading the wrong half produces an environment that never becomes ready. Database and queue images are recognised as infrastructure rather than services to build, including behind a registry prefix. Dotenv files are read for names only, never values. An example file sometimes contains a real credential by accident, and reading the value would carry it into a finding, then an event, then a log. A test plants one and asserts it appears nowhere. Failure paths covered: a transitive dependency creates a rule: TestRun_SkipsDependencyAndBuildDirectories a test fixture is mistaken for the app: TestRun_SkipsTestAndExampleDockerfiles one malformed file loses the repository: TestRun_MalformedPackageJsonIsReportedNotFatal an analyzer fails: TestRun_AnAnalyzerThatFailsDoesNotLoseTheOthers the budget runs out: TestRun_ReportsPartialResultsWhenTheBudgetRunsOut a host port is read as the container port: TestRun_ComposePublishesTheContainerPortNotTheHostPort sources disagree about a port: TestRun_AsksWhenSourcesDisagreeAboutAPort output shuffles between runs: TestRun_IsDeterministic a credential in an example file leaks: TestRun_NeverReadsAValueFromAnExampleFile a huge checked in file: TestRun_LargeFilesAreSkippedAndReported Fuzzed 416,000 executions across package.json, Dockerfile, and compose inputs with no crashes. Coverage 80.8 percent. Signed-off-by: Vir Sanghavi <Virrsanghavi@gmail.com>
V
Vir Sanghavi committed
0615766bb9b6728b4ca2d9525a8632a1ef4cf31e
Parent: 318f08e