Add named class (build-stage) extraction for dockerfile - #1988
Merged
Conversation
Dockerfile had no named class extraction at all: it wasn't in _CLASS_START_NAMED_EXTRACTION_LANGS, so it fell back to the legacy generic regex (class|struct|interface|trait|enum), which never matches Dockerfile's FROM syntax at all -- correctly, since Dockerfile has none of those keywords. Result: class_data stayed completely empty for every Dockerfile scanned, even though struct_class_start (the raw signal) correctly counted every real FROM line. class_start's own regex previously captured only the literal keyword FROM itself, not the build stage's real name -- extended it to an alternation shape (matching the existing Fortran/Lua/ABAP convention _resolve_class_start_match already documents): group 1 captures the AS <alias> name when present, group 2 falls back to the bare base-image reference for a stage with no alias (the file's final/default stage). --platform=$VAR-style flags between FROM and the image reference are skipped over. Added dockerfile to _CLASS_START_NAMED_EXTRACTION_LANGS, and to the boundary-resolution skip condition alongside abap (same #1907 rationale -- Dockerfile stages are never nested, and the brace search would mistake ${VAR} template-substitution braces for a real body opener). Verified: class_data row counts now match struct_class_start exactly (69/6/1/1 across the moby test corpus), with real stage names (base, criu, xx, binary-dummy, ...) instead of the literal string "FROM" repeated. ReDoS-probed the new regex directly (5000-char runs, deeply nested continuations, huge flag/image tokens) -- all sub-millisecond. One known, accepted side effect: THE LINEAGE EXTRACTOR (detector.py's generic "any class_start match with 2+ groups treats group 2 as an inheritance parent") doesn't know groups 1/2 here are alternation- exclusive, not name-then-parent -- a bare FROM <image> (no alias) sweeps the image reference into that file's parent_entity metadata. This is a pre-existing, already-shipped pattern (fortran's own class_start has the identical alternation shape and triggers the same behavior for bare TYPE declarations today), not something this fix introduces -- tracked as its own issue (#1983) for a future, more alternation-aware fix to the shared extractor, not blocking here. Fixes #1974. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Co-Authored-By: Gemini 3.1 Pro <noreply@google.com>
squid-protocol
enabled auto-merge (squash)
August 21, 2026 01:52
…start-1974 # Conflicts: # tests/golden_master_audit.json # tests/golden_master_zero_dep_audit.json
…start-1974 # Conflicts: # tests/golden_master_audit.json # tests/golden_master_zero_dep_audit.json
…-shift Second conflict round: #1987 (jcl/m4) landed on main right after the first resolution, requiring another regenerate-and-rebless pass. Also fixes a real, separate issue caught while investigating why PR #1985/#1987 merged despite a failing ruff-audit CI check: #1972 (record_keeper.py's class_count fix, merged earlier and unrelated to this PR) shifted 7 pre-existing baselined findings by a couple of lines each -- tests/ruff_audit_baseline.json was never regenerated to absorb that shift, so every PR's ruff-audit CI run since has been reporting those 7 as spurious 'new' findings. Confirmed via audit_check.py that all 7 are pure line-shifts (same file/code/ message, just moved), not real regressions, and regenerated the baseline to absorb them. Verified against the exact CI-pinned ruff version (0.16.0, not whatever floats on PATH locally).
Contributor
🐦⬛ Muninn Security Scan
ℹ️ Info Findings[checkov] Ensure that a user for the container has been createdFile: [checkov] Ensure that HEALTHCHECK instructions have been added to container imagesFile: [checkov] Ensure the base image uses a non latest version tagFile: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Dockerfile had no named class (build-stage) extraction at all: it wasn't in
_CLASS_START_NAMED_EXTRACTION_LANGS, so it fell back to the legacy generic regex (class|struct|interface|trait|enum), which never matches Dockerfile'sFROMsyntax -- correctly, since Dockerfile has none of those keywords. Result:class_datastayed completely empty for every Dockerfile scanned, even thoughstruct_class_start(the raw signal) correctly counted every realFROMline (confirmed while manually verifying dockerfile's tri-comparison numbers for #1976:file_data.class_countreported 69 for a Dockerfile with 69 real stages, butclass_datahad 0 rows).Fix
class_start's own regex previously captured only the literal keywordFROMitself, not the build stage's real name. Extended it to an alternation shape (matching the existing Fortran/Lua/ABAP convention_resolve_class_start_match's own docstring documents):AS <alias>name when present.--platform=$VAR-style flags betweenFROMand the image reference are skipped over.Added
dockerfileto_CLASS_START_NAMED_EXTRACTION_LANGS, and to the boundary-resolution skip condition alongsideabap(same #1907 rationale -- Dockerfile stages are never nested, and the brace search would mistake${VAR}template-substitution braces for a real body opener).Verification
pytest tests/extraction/languages/test_dockerfile.py tests/extraction/languages/test_dockerfile_strict.py: 125 passed.class_datarow counts now matchstruct_class_startexactly (69/6/1/1), with real stage names (base,criu,xx,binary-dummy, ...) appearing instead of the literal stringFROMrepeated.ruff_audit.py --ci/mypy_audit.py --ci: clean against baseline.crucible_check.py(full_precision+zero_dependency): drift limited to exactly 3 newParent Entitykeys (see below) -- both golden masters re-blessed; final re-run: PASS/PASS.One known, accepted side effect (tracked separately, not blocking)
THE LINEAGE EXTRACTOR (
detector.py's generic "anyclass_startmatch with 2+ capture groups treats group 2 as an inheritance parent") doesn't know groups 1/2 here are alternation-exclusive (name-OR-name), not name-then-parent -- a bareFROM <image>(no alias) sweeps the image reference into that file'sparent_entitymetadata. Confirmed this is a pre-existing, already-shipped pattern, not something this fix introduces: fortran's ownclass_starthas the identical alternation shape (MODULE|INTERFACEname in group 1, bareTYPEname in group 2) and triggers the same behavior for bareTYPEdeclarations today, in production. Filed as its own issue (#1983) for a future, more alternation-aware fix to the shared extractor.Implemented via a Gemini/agy dispatch from a fully diagnosed issue, independently re-verified (diff read line-by-line, tests/audits/extraction results/ReDoS probe/
crucible_check.pyall re-run from scratch) before this PR.Fixes #1974.
🤖 Generated with Claude Code