Repository navigation
Add controlled transformation experiment for finding identity (RQ2) - #54
Merged
Merged
Conversation
…identity Real histories rarely edit the flagged line and introduce few new findings, so they cannot show which kinds of change break which identity schemes (RQ2). transforms.py rewrites one Python file around one finding and returns the new text plus an exact old-line to new-line map (ground truth by construction): reformat, insert_top, insert_below, rename_local, rename_function, move_function, change_literal, extract_helper, duplicate_above and duplicate_below, using libcst for source-preserving renames. An optional ai_refactor asks a language model (model and key from flags or environment) to refactor the enclosing function, caches replies on disk, uses the line diff as ground truth and flags every case for manual review. The new 'transform' subcommand samples files with findings, applies every transformation, scans before and after, and writes the invariance matrix (transformation x scheme, false-new and hidden rates) as CSV and Markdown, with an optional matplotlib heatmap. Extra dependencies live in research/finding_identity/requirements.txt. Signed-off-by: Vichitra Attri <mevichitra@gmail.com>
5 of 11 tasks
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
Real commit histories rarely edit the flagged line and introduce few new findings, so the replay harness cannot say which kinds of change break which identity schemes (RQ2). This PR adds a controlled experiment.
research/finding_identity/transforms.pyrewrites one Python file around one finding and returns the new text plus an exact old-line → new-line map, which is the ground truth by construction. A newtransformsubcommand samples files with findings, applies every transformation, scans before and after, and writes an invariance matrix (transformation × scheme, false-new and hidden rates) as CSV and Markdown, with an optional matplotlib heatmap. Research code only; nothing in thecodeguardpackage changes.Type of change
research/)Checklist
git commit -s— DCO sign-off on all commitspytestpasses locally (652 passed)ruff check src/ tests/clean;ruff check research/andruff format --check research/cleanmypy src/clean;MYPYPATH=src mypy --strict research/finding_identitycleanresearch/need none)Transformations
reformatblackat line length 79 (ruff formatif black is missing); the line map pairs the syntax trees before and after, so a finding is followed into a split lineinsert_top__future__importsinsert_belowrename_localrename_functionmove_functionchange_literal_v2appended to a string literal on the line, or a number incrementedextract_helperduplicate_above/duplicate_belowai_refactor--ai-model/FINDING_IDENTITY_AI_MODELand--ai-api-key/ANTHROPIC_API_KEY; replies cached in.cache/ai/; ground truth is thelinemap.pydiff and every case is flagged for manual reviewInvariance matrix
Command:
python -m research.finding_identity transform <repo> --samples 200 --heatmap, seed 0, all rule categories, CodeGuard 2.2.0.extract_helpercase in DefectDojo that introduced a finding), the cell reads false-new % / hidden %. Hidden % is the share of introduced findings matched to an old finding.DefectDojo (
dev@559a677, 200 files with findings)Flask (
main@d086db8, all 12 files with findings)What the matrix says
linebreaks on every move (insertions above, moved functions, reformatting).githubbreaks on edits after the flagged line (insert_below: 88.5%).codeguardbreaks on renames of the enclosing function or of a local on the line, and on extraction. Onlycg_no_scopesurvivesrename_functionandextract_helper.codeguardandcg_no_scopekeep everychange_literalcase, whileline_text,githubandcg_rawlose almost all of them.github(100% hidden), and the original is reported as new. GitHub's forward-reading hash fails the other way: a copy below takes over the original's hash (92% hidden).codeguard's 15.8% hidden below comes from keys that already collide with another finding in the file.rename_functionhits text schemes only through thedefline. The 67.7% forline_text/githubcomes from findings reported on thedefline itself (CG-QUAL-003,CG-QUAL-005).extract_helperlost the target in 145 attempts:CG-PERF-002118,CG-PERF-00525, plus one each ofCG-SEC-001andCG-SEC-014. Outside its loop the statement is no longer a finding. Lost targets are counted, not scored, and the next finding in the file is tried, which leaves only 15 scored cases for that row.Test evidence
tests/test_transforms.pychecks, for each deterministic transformation:ai_refactorworks with a fake model: caching, offline replay, rejection of a reply that renames the function.tests/test_experiment.pyruns the whole experiment end to end on a small fixture directory (expected matrix cells), the CLI subcommand, ground-truth resolution for split lines, and the lost-target path.Notes for reviewer
cli.pychanges by 6 additive lines, to stay mergeable with parallel work: the subcommand lives intransform_cli.py, which imports the experiment lazily, soreplayandsummarizedon't need libcst.libcst,black, optionalmatplotlibandanthropic) are inresearch/finding_identity/requirements.txt, not inpyproject.toml.--transforms allincludes it only when a model is configured. No model identifier is written in the repository.deforexceptlines (complexity, parameters, swallowed exceptions). The statement-level transformations do not fit those findings, so theirnis smaller. Reasons for every "n/a" are recorded insummary.json.reformatrarely touches the target in Flask. Flask is already formatted at 88 columns, so black at 79 columns changes few lines and seldom touches the target.results/,.repos/,.cache/).