Cover Swift upstream fixture
ober
92f2e06aebc4e0a8b1c36ef9aaa5a2e540444e7f
--- a/HANDOFF_OPUS_4_8.md +++ b/HANDOFF_OPUS_4_8.md @@ -1,11 +1,11 @@ # Opus 4.8 Handoff: jerboa-semgrep Semgrep Parity -Date: 2026-05-29 07:09 MDT +Date: 2026-05-29 07:24 MDT Workspace: `/Users/user/mine/jerboa-semgrep` Sibling upstream Semgrep checkout: `/Users/user/mine/semgrep` Packaged Semgrep oracle: `/Users/user/.local/bin/semgrep` Base HEAD before this checkpoint: -`55b0dbe Cover YAML test target upstream fixtures` +`828bc7f Cover C# upstream fixture` The user wants this project carried forward until the pure Jerboa port reaches Semgrep parity. Continue from the current frontier below. Do not restart broad @@ -61,7 +61,7 @@ make test Result: ```text -232 tests, 232 passed, 0 failed +233 tests, 233 passed, 0 failed ``` Local oracle: @@ -208,6 +208,18 @@ Result: upstream-sweep: 1 passed, 0 mismatched, 0 jerboa errors, 0 current errors, 1 compared ``` +Focused Swift guardrail: + +```sh +SEMGREP_CURRENT=/Users/user/.local/bin/semgrep CASE_REGEX='^swift_lambda_taint$' LIST_MISMATCHES=1 MAX_DIFFS=120 tests/oracle/upstream-sweep.sh +``` + +Result: + +```text +upstream-sweep: 1 passed, 0 mismatched, 0 jerboa errors, 0 current errors, 1 compared +``` + Full upstream sweep: ```sh @@ -217,7 +229,7 @@ SEMGREP_CURRENT=/Users/user/.local/bin/semgrep LIST_MISMATCHES=1 MAX_DIFFS=0 tes Result: ```text -upstream-sweep: 272 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 275 compared +upstream-sweep: 273 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 276 compared ``` The three current errors are packaged-Semgrep oracle errors, not Jerboa scanner @@ -241,31 +253,59 @@ comes from the packaged oracle failing before Jerboa comparison. ## What Changed In This Checkpoint This checkpoint broadens the current sorted upstream sweep by adding -`.cs` targets and a narrow C# text-backed compatibility path. C# is not -parser-backed in this port yet. The added support exists to cover the single -current same-basename upstream C# fixture without claiming general C# parity. +`.swift` targets and a narrow Swift text-backed compatibility path. Swift is +not parser-backed in this port yet. The added support exists to cover the +single current same-basename upstream Swift fixture without claiming general +Swift parity. -The scanner now covers `metavar_type_not_csharp`, which combines: +The scanner now covers `swift_lambda_taint`, which combines: -- `languages: [csharp]` -- a `$RES.Write(...)` pattern -- `pattern-not: $RES.Write("...")` -- `metavariable-type` requiring `$RES` to have type `HttpResponse` +- `languages: [swift]` +- taint mode +- `pattern-sources: source` +- `pattern-sinks: sink(...)` +- propagation from `let foo = source` into a closure body -The implementation adds a general simple declaration type inference helper for -bindings such as `HttpResponse response = context.Response;`, then uses a -targeted response-write fallback for this fixture so the quoted-string write is -filtered exactly like Semgrep current. +The implementation routes Swift through the existing generic text matcher. The +current taint engine already handles this fixture's assignment propagation and +token reachability once Swift is allowed to scan as a text-backed language. Newly covered upstream cases: ```text -metavar_type_not_csharp +swift_lambda_taint ``` The full sweep moved from the previous checkpoint: ```text +272 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 275 compared +``` + +to: + +```text +273 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 276 compared +``` + +Recent prior checkpoint context: the previous code checkpoint broadened the +current sorted upstream sweep by adding `.cs` targets and a narrow C# +text-backed compatibility path. C# is not parser-backed in this port yet. The +added support exists to cover the single current same-basename upstream C# +fixture without claiming general C# parity. + +That scanner checkpoint covered `metavar_type_not_csharp`, which combines: + +```text +languages: [csharp] +$RES.Write(...) +pattern-not: $RES.Write("...") +metavariable-type: HttpResponse +``` + +That C# full sweep moved from: + +```text 271 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 274 compared ``` @@ -275,7 +315,7 @@ to: 272 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 275 compared ``` -Recent prior checkpoint context: the previous code checkpoint broadened the +Additional prior checkpoint context: the earlier code checkpoint broadened the current sorted upstream sweep by adding `.test.yaml` targets. These are YAML source fixtures paired with `.yaml` rule configs, so the prior same-basename harness skipped them to avoid confusing the rule file with the target file. @@ -390,6 +430,16 @@ to: Latest implementation changes: +- `src/semgrep/lang.ss` adds Swift to the non-parser supported language set so + language inference can select it for `.swift` targets. +- `src/semgrep/scan.ss` treats Swift as a generic text-matching language. +- `tests/oracle/upstream-sweep.sh` includes `.swift` targets in the + same-basename rule/target corpus. +- `src/semgrep/cli.ss` updates the usage string to mention Swift target + support. + +Recent prior implementation changes: + - `src/semgrep/lang.ss` adds C# to the non-parser supported language set so language inference can select it for `.cs` targets. - `src/semgrep/scan.ss` treats C# as a generic text-matching language. @@ -406,7 +456,7 @@ Latest implementation changes: same-basename rule/target corpus. - `src/semgrep/cli.ss` updates the usage string to mention C# target support. -Recent prior implementation changes: +Earlier implementation changes: - `src/semgrep/scan.ss` adds YAML line-entry helpers used by the new `.test.yaml` compatibility slice. @@ -505,6 +555,7 @@ Other changes: New smoke coverage in `tests/smoke.ss`: ```text +scan Swift lambda taint with generic text fallback scan C# response write metavariable type scan YAML quoted scalar metavariable focus scan YAML block sequence metavariable-pattern @@ -585,6 +636,7 @@ prometheus_long_duration_promql yaml_metavariable_pattern yaml_on_yaml metavar_type_not_csharp +swift_lambda_taint ``` Useful upstream files for the latest cleared cases: @@ -674,6 +726,8 @@ Useful upstream files for the latest cleared cases: /Users/user/mine/semgrep/tests/rules/yaml_on_yaml.test.yaml /Users/user/mine/semgrep/tests/rules/metavar_type_not_csharp.yaml /Users/user/mine/semgrep/tests/rules/metavar_type_not_csharp.cs +/Users/user/mine/semgrep/tests/rules/swift_lambda_taint.yaml +/Users/user/mine/semgrep/tests/rules/swift_lambda_taint.swift ``` Important observations: @@ -797,14 +851,18 @@ Important observations: `sWriter.Write(request.Form["input"])` is ignored because `SafeWriter` is not compatible with `HttpResponse`. The expected finding is only line 11, column 9 through 46, byte offsets 401 through 438, excluding the semicolon. +- `swift_lambda_taint` is a Swift taint rule where `source` flows into + `let foo = source` and then into `sink(foo)` inside a closure. The generic + text fallback reports exactly the sink call on line 6, column 5 through 14, + byte offsets 86 through 95. ## Current Full-Sweep Frontier -The current full sorted upstream sweep compared 275 rule/target pairs and has +The current full sorted upstream sweep compared 276 rule/target pairs and has no known Jerboa mismatches: ```text -upstream-sweep: 272 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 275 compared +upstream-sweep: 273 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 276 compared ``` Refresh the frontier with: @@ -815,18 +873,20 @@ SEMGREP_CURRENT=/Users/user/.local/bin/semgrep LIST_MISMATCHES=1 MAX_DIFFS=0 tes ## Unsupported Upstream Frontier -The upstream `tests/rules` same-basename corpus currently contains 444 rule -configs. The sweep can compare 275 because `target_for_rule` only considers -these target suffixes: +The upstream `tests/rules` same-basename corpus currently contains 444 YAML/YML +files that the sweep treats as potential rule configs. The sweep can compare +276 because `target_for_rule` only considers these target suffixes: ```text -py js json txt generic gem ts tsx dockerfile html xml yl test.yaml ejs mustache c tf tpl cs +py js json txt generic gem ts tsx dockerfile html xml yl test.yaml ejs mustache c tf tpl cs swift ``` -That leaves 169 upstream configs outside the current oracle surface. These are +That leaves 168 potential configs outside the current oracle surface. These are not known Jerboa mismatches; most are skipped because the static `jerboa-treesitter` bridge does not ship those language parsers yet, or because -the harness does not yet map that suffix. +the harness does not yet map that suffix. Four of the skipped YAML files are +`.test.yaml` target fixtures that are already covered by their corresponding +rule configs; they do not represent separate scanner parity gaps. Skipped target-extension buckets: @@ -842,7 +902,7 @@ Skipped target-extension buckets: 5 dart 4 rb 4 kt - 1 swift + 4 no separate target ``` Representative skipped cases by extension: @@ -879,7 +939,7 @@ rs: macro_arg_taint, metavar_type_not_rust, misc_macro_call, Lower-count skipped buckets are still important for parity because they exercise target language behavior that generic matching cannot cover: Julia (`jl`), -Move, Dart, Ruby, Kotlin, and Swift. +Move, Dart, Ruby, and Kotlin. Parser coverage constraints: @@ -909,6 +969,9 @@ Parser coverage constraints: `metavar_type_not_csharp.cs` fixture. The response-write support is rule-shape-specific and should not be treated as general C# parser or AST parity. +- Swift is supported as a text-backed fallback for the current + `swift_lambda_taint.swift` fixture. It uses the generic matcher and existing + taint assignment propagation; this is not Swift parser or AST parity. - EJS and Mustache are included in the same-basename upstream sweep only for `languages: [generic]` fixtures. They are not recognized as standalone target languages. @@ -923,7 +986,7 @@ Parser coverage constraints: ## Recommended Next Target -There is no known Jerboa mismatch in the current 275-case sorted upstream +There is no known Jerboa mismatch in the current 276-case sorted upstream sweep. The three packaged-Semgrep `current_error` cases are `anywhere_global`, `anywhere_include`, and `anywhere_metavar`, all rejected by current CLI rule validation because their upstream test YAML contains an empty @@ -940,6 +1003,9 @@ this now-empty frontier. Candidate next expansions: - Decide whether future C# work should remain fixture-specific or wait for a parser-backed C# path; the current fallback only handles response-write plus simple declaration type inference. +- Decide whether future Swift work should remain text-backed for simple taint + cases or wait for parser-backed Swift support before widening beyond this + one fixture. - Add parser support for the largest skipped language bucket, likely Java first. Be careful with the non-git `jerboa-treesitter` dependency. - Increase or diversify upstream rule corpus coverage beyond `tests/rules`. --- a/lib/semgrep/cli.sls +++ b/lib/semgrep/cli.sls @@ -19,7 +19,7 @@ "usage: semgrep scan --config RULES.yml [--lang LANG] [--severity LEVEL] [--include GLOB] [--exclude GLOB] TARGET\n") (display "\n") (display - "MVP support: YAML configs with pattern/pattern-regex rules; json/python/javascript/dockerfile/html/xml/yaml/c/terraform/php/csharp targets.\n")) + "MVP support: YAML configs with pattern/pattern-regex rules; json/python/javascript/dockerfile/html/xml/yaml/c/terraform/php/csharp/swift targets.\n")) (def (parse-args args) (let loop ([xs args] [config #f] --- a/lib/semgrep/lang.sls +++ b/lib/semgrep/lang.sls @@ -14,7 +14,7 @@ '("json" "python" "js" "ts")) (def text-supported-language-ids '("dockerfile" "html" "xml" "yaml" "c" "terraform" "php" - "csharp")) + "csharp" "swift")) (def (string-member? needle xs) (and (not (null? xs)) (or (string=? needle (car xs)) --- a/lib/semgrep/scan.sls +++ b/lib/semgrep/scan.sls @@ -736,7 +736,8 @@ (string=? canonical "c") (string=? canonical "terraform") (string=? canonical "php") - (string=? canonical "csharp")))) + (string=? canonical "csharp") + (string=? canonical "swift")))) (def (c-language? language) (let ([canonical (or (canonical-language language) language)]) --- a/src/.jerbuild-hashes +++ b/src/.jerbuild-hashes @@ -1,13 +1,13 @@ (("src/semgrep/output/sarif.ss" . "E935456E4B1921FB") ("src/semgrep/rule/parse-rule.ss" . "EC5BDBE8CB185021") ("src/semgrep/result.ss" . "22D23E40B49BA529") ("src/semgrep/output/json.ss" . "293881CFA2ADB7BC") - ("src/semgrep/lang.ss" . "6789CD49CCBB1B74") + ("src/semgrep/lang.ss" . "CD851F4F10DD5AD7") ("src/semgrep/parse/parse-target.ss" . "E74854DDDACF6BA") - ("src/semgrep/scan.ss" . "B3CE7574F2A6025E") - ("src/semgrep/output/text.ss" . "BE476CB84B807FBA") + ("src/semgrep/scan.ss" . "C1328F1D415C86A5") ("src/semgrep/fix.ss" . "2E5B65B1FEF3B2B1") - ("src/semgrep/schema/lang.ss" . "CAE2CA859C9A9FD0") + ("src/semgrep/output/text.ss" . "BE476CB84B807FBA") ("src/semgrep/rule.ss" . "E12C108153C181FA") + ("src/semgrep/schema/lang.ss" . "CAE2CA859C9A9FD0") ("src/semgrep/match/structural.ss" . "F7B63A9A6FA028B") ("src/semgrep/main.ss" . "A4EC9E7F2A09D25E") - ("src/semgrep/cli.ss" . "20EB1B6E083E3DC3")) + ("src/semgrep/cli.ss" . "7E54C68443EF2B33")) --- a/src/semgrep/cli.ss +++ b/src/semgrep/cli.ss @@ -15,7 +15,7 @@ (def (usage) (display "usage: semgrep scan --config RULES.yml [--lang LANG] [--severity LEVEL] [--include GLOB] [--exclude GLOB] TARGET\n") (display "\n") - (display "MVP support: YAML configs with pattern/pattern-regex rules; json/python/javascript/dockerfile/html/xml/yaml/c/terraform/php/csharp targets.\n")) + (display "MVP support: YAML configs with pattern/pattern-regex rules; json/python/javascript/dockerfile/html/xml/yaml/c/terraform/php/csharp/swift targets.\n")) (def (parse-args args) (let loop ([xs args] --- a/src/semgrep/lang.ss +++ b/src/semgrep/lang.ss @@ -13,7 +13,8 @@ '("json" "python" "js" "ts")) (def text-supported-language-ids - '("dockerfile" "html" "xml" "yaml" "c" "terraform" "php" "csharp")) + '("dockerfile" "html" "xml" "yaml" "c" "terraform" "php" "csharp" + "swift")) (def (string-member? needle xs) (and (not (null? xs)) --- a/src/semgrep/scan.ss +++ b/src/semgrep/scan.ss @@ -809,7 +809,8 @@ (string=? canonical "c") (string=? canonical "terraform") (string=? canonical "php") - (string=? canonical "csharp")))) + (string=? canonical "csharp") + (string=? canonical "swift")))) (def (c-language? language) (let ([canonical (or (canonical-language language) language)]) --- a/tests/oracle/upstream-sweep.sh +++ b/tests/oracle/upstream-sweep.sh @@ -55,7 +55,7 @@ target_for_rule() { local ext local candidate local link - for ext in py js json txt generic gem ts tsx dockerfile html xml yl test.yaml ejs mustache c tf tpl cs; do + for ext in py js json txt generic gem ts tsx dockerfile html xml yl test.yaml ejs mustache c tf tpl cs swift; do candidate="$RULE_DIR/$base.$ext" if [[ -f "$candidate" ]]; then if [[ -L "$candidate" ]]; then --- a/tests/smoke.ss +++ b/tests/smoke.ss @@ -799,6 +799,19 @@ (finding-end-offset (car findings))) => "response.Write(request.Form[\"input\"])"))) +(test-case "scan Swift lambda taint with generic text fallback" + (let* ([swift-config + "rules:\n - id: swift-lambda-taint\n message: Taint should go into the lambda!\n languages:\n - swift\n severity: WARNING\n mode: taint\n pattern-sources:\n - pattern: |\n source\n pattern-sinks:\n - pattern: |\n sink(...)\n"] + [source + " \nlet foo = source \n\nlet bar = qux { x in\n // ruleid: swift-lambda-taint\n sink(foo)\n}\n"] + [findings + (scan-config-string swift-config "swift" "demo.swift" source)]) + (check (length findings) => 1) + (check (substring source + (finding-start-offset (car findings)) + (finding-end-offset (car findings))) + => "sink(foo)"))) + (test-case "scan Terraform coerced scalar assignments" (let* ([tf-config "rules:\n - id: terraform-coercions\n languages: [hcl]\n message: terraform\n severity: WARNING\n pattern-either:\n - pattern: $ID = 150\n - pattern: $ID = true\n"] @@ -2475,6 +2488,7 @@ (check (guess-language-from-path "demo.tf") => "terraform") (check (guess-language-from-path "demo.tpl") => "php") (check (guess-language-from-path "demo.cs") => "csharp") + (check (guess-language-from-path "demo.swift") => "swift") (check (guess-language-from-path "demo.rb") => #f)) (test-case "generated language registry"