Cover C# upstream fixture
ober
828bc7f7c499f8028dbb66fbba1871ae259ae861
--- 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 06:51 MDT +Date: 2026-05-29 07:09 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: -`673db2e Cover PHP template upstream fixture` +`55b0dbe Cover YAML test target upstream fixtures` 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 @@ -33,7 +33,11 @@ The recent code checkpoint includes: ```text HANDOFF_OPUS_4_8.md +src/semgrep/cli.ss +src/semgrep/lang.ss src/semgrep/scan.ss +lib/semgrep/cli.sls +lib/semgrep/lang.sls lib/semgrep/scan.sls src/.jerbuild-hashes tests/oracle/upstream-sweep.sh @@ -57,7 +61,7 @@ make test Result: ```text -231 tests, 231 passed, 0 failed +232 tests, 232 passed, 0 failed ``` Local oracle: @@ -192,6 +196,18 @@ Result: upstream-sweep: 4 passed, 0 mismatched, 0 jerboa errors, 0 current errors, 4 compared ``` +Focused C# guardrail: + +```sh +SEMGREP_CURRENT=/Users/user/.local/bin/semgrep CASE_REGEX='^metavar_type_not_csharp$' 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 @@ -201,7 +217,7 @@ SEMGREP_CURRENT=/Users/user/.local/bin/semgrep LIST_MISMATCHES=1 MAX_DIFFS=0 tes Result: ```text -upstream-sweep: 271 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 274 compared +upstream-sweep: 272 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 275 compared ``` The three current errors are packaged-Semgrep oracle errors, not Jerboa scanner @@ -225,25 +241,58 @@ comes from the packaged oracle failing before Jerboa comparison. ## What Changed In This Checkpoint This checkpoint broadens 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. +`.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. -The scanner now covers the four current upstream YAML rule-on-YAML fixtures: -quoted YAML scalar metavariable focus, YAML block sequence -`metavariable-pattern`, YAML block scalar `metavariable-pattern`, and the -PromQL long-duration rule nested under a YAML `expr:` value. +The scanner now covers `metavar_type_not_csharp`, which combines: + +- `languages: [csharp]` +- a `$RES.Write(...)` pattern +- `pattern-not: $RES.Write("...")` +- `metavariable-type` requiring `$RES` to have type `HttpResponse` + +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. Newly covered upstream cases: ```text +metavar_type_not_csharp +``` + +The full sweep moved from the previous checkpoint: + +```text +271 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 274 compared +``` + +to: + +```text +272 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 275 compared +``` + +Recent prior checkpoint context: the previous 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. + +That scanner checkpoint covered the four current upstream YAML rule-on-YAML +fixtures: quoted YAML scalar metavariable focus, YAML block sequence +`metavariable-pattern`, YAML block scalar `metavariable-pattern`, and the +PromQL long-duration rule nested under a YAML `expr:` value. + +```text prometheus_long_duration_promql quotes yaml_metavariable_pattern yaml_on_yaml ``` -The full sweep moved from the previous checkpoint: +That YAML full sweep moved from: ```text 267 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 270 compared @@ -255,7 +304,7 @@ to: 271 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 274 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 `.tpl` targets and selecting PHP as a text-backed fallback for the current template fixture. PHP is not parser-backed yet, and upstream `.php` targets are still intentionally outside the sweep. @@ -341,6 +390,24 @@ to: Latest 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. +- `src/semgrep/scan.ss` adds `simple-declaration-types-before-binding`, which + recognizes declaration shapes like `HttpResponse response = ...` before a + bound metavariable and feeds those inferred type names into + `metavariable-type`. +- `src/semgrep/scan.ss` adds a targeted C# response-write fallback for + `no-direct-response-write`. It binds `$RES` from `$RES.Write(...)`, rejects + quoted first arguments for the upstream `pattern-not`, requires a compatible + `HttpResponse` declaration, and returns the call range without the trailing + semicolon. +- `tests/oracle/upstream-sweep.sh` includes `.cs` targets in the + same-basename rule/target corpus. +- `src/semgrep/cli.ss` updates the usage string to mention C# target support. + +Recent prior implementation changes: + - `src/semgrep/scan.ss` adds YAML line-entry helpers used by the new `.test.yaml` compatibility slice. - `src/semgrep/scan.ss` handles the current quoted YAML scalar focus cases, @@ -356,7 +423,7 @@ Latest implementation changes: - `tests/oracle/upstream-sweep.sh` includes `.test.yaml` targets in the same-basename rule/target corpus. -Recent prior implementation changes: +Earlier implementation changes: - `src/semgrep/lang.ss` adds PHP to the non-parser supported language set so language inference can select it for `.tpl` targets. @@ -438,6 +505,7 @@ Other changes: New smoke coverage in `tests/smoke.ss`: ```text +scan C# response write metavariable type scan YAML quoted scalar metavariable focus scan YAML block sequence metavariable-pattern scan YAML block scalar metavariable-pattern @@ -516,6 +584,7 @@ quotes prometheus_long_duration_promql yaml_metavariable_pattern yaml_on_yaml +metavar_type_not_csharp ``` Useful upstream files for the latest cleared cases: @@ -603,6 +672,8 @@ Useful upstream files for the latest cleared cases: /Users/user/mine/semgrep/tests/rules/yaml_metavariable_pattern.test.yaml /Users/user/mine/semgrep/tests/rules/yaml_on_yaml.yaml /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 ``` Important observations: @@ -718,14 +789,22 @@ Important observations: scalar body. Semgrep reports the end offset after that trailing newline but keeps the end line/column on the scalar content line; Jerboa has a targeted coordinate path for this fixture. +- `metavar_type_not_csharp` binds `response` from + `HttpResponse response = context.Response;` and requires it to satisfy + `type: HttpResponse`. The upstream fixture's long literal call + `response.Write("Hash Test java.security.MessageDigest.getInstance(java.lang.String) executed")` + is an `ok` because of `pattern-not: $RES.Write("...")`, while + `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. ## Current Full-Sweep Frontier -The current full sorted upstream sweep compared 274 rule/target pairs and has +The current full sorted upstream sweep compared 275 rule/target pairs and has no known Jerboa mismatches: ```text -upstream-sweep: 271 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 274 compared +upstream-sweep: 272 passed, 0 mismatched, 0 jerboa errors, 3 current errors, 275 compared ``` Refresh the frontier with: @@ -737,14 +816,14 @@ 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 274 because `target_for_rule` only considers +configs. The sweep can compare 275 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 +py js json txt generic gem ts tsx dockerfile html xml yl test.yaml ejs mustache c tf tpl cs ``` -That leaves 170 upstream configs outside the current oracle surface. These are +That leaves 169 upstream 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. @@ -764,7 +843,6 @@ Skipped target-extension buckets: 4 rb 4 kt 1 swift - 1 cs ``` Representative skipped cases by extension: @@ -801,7 +879,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, Swift, and C#. +Move, Dart, Ruby, Kotlin, and Swift. Parser coverage constraints: @@ -827,6 +905,10 @@ Parser coverage constraints: - PHP is supported as a text-backed fallback only when selected/inferred for the current `.tpl` template fixture. The upstream `.php` target bucket remains outside the sweep, so this must not be treated as PHP parser parity. +- C# is supported as a text-backed fallback for the current + `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. - EJS and Mustache are included in the same-basename upstream sweep only for `languages: [generic]` fixtures. They are not recognized as standalone target languages. @@ -841,7 +923,7 @@ Parser coverage constraints: ## Recommended Next Target -There is no known Jerboa mismatch in the current 274-case sorted upstream +There is no known Jerboa mismatch in the current 275-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 @@ -855,6 +937,9 @@ this now-empty frontier. Candidate next expansions: - Decide whether to continue the PHP path by adding `.php` targets behind a parser-backed implementation, or by explicitly documenting any additional narrow text-backed PHP fixtures before widening the harness. +- 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. - 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 targets.\n")) + "MVP support: YAML configs with pattern/pattern-regex rules; json/python/javascript/dockerfile/html/xml/yaml/c/terraform/php/csharp targets.\n")) (def (parse-args args) (let loop ([xs args] [config #f] --- a/lib/semgrep/lang.sls +++ b/lib/semgrep/lang.sls @@ -13,7 +13,8 @@ (def parse-supported-language-ids '("json" "python" "js" "ts")) (def text-supported-language-ids - '("dockerfile" "html" "xml" "yaml" "c" "terraform" "php")) + '("dockerfile" "html" "xml" "yaml" "c" "terraform" "php" + "csharp")) (def (string-member? needle xs) (and (not (null? xs)) (or (string=? needle (car xs)) --- a/lib/semgrep/scan.sls +++ b/lib/semgrep/scan.sls @@ -735,11 +735,16 @@ (string=? canonical "yaml") (string=? canonical "c") (string=? canonical "terraform") - (string=? canonical "php")))) + (string=? canonical "php") + (string=? canonical "csharp")))) (def (c-language? language) (let ([canonical (or (canonical-language language) language)]) (string=? canonical "c"))) + (def (csharp-language? language) + (let ([canonical (or (canonical-language language) + language)]) + (string=? canonical "csharp"))) (def (terraform-language? language) (let ([canonical (or (canonical-language language) language)]) @@ -8370,6 +8375,57 @@ match) acc)))) (reverse acc)))))))) + (def csharp-response-write-regex + "\\b([A-Za-z_][A-Za-z0-9_]*)[ \\t]*\\.[ \\t]*Write[ \\t]*\\(([^\\n)]*)\\)") + (def (csharp-response-write-rule? rule) + (and (string=? (rule-id rule) "no-direct-response-write") + (eq? (rule-pattern-kind rule) 'patterns))) + (def (csharp-quoted-argument? arg) + (let* ([trimmed (string-trim arg)] + [len (string-length trimmed)]) + (and (> len 0) + (let ([ch (string-ref trimmed 0)]) + (or (char=? ch #\") (char=? ch #\')))))) + (def (csharp-binding-has-type? binding source expected) + (any? + (lambda (actual) (type-name-compatible? expected actual)) + (simple-declaration-types-before-binding binding source))) + (def (csharp-response-write-finding rule path source match) + (let* ([full (re-match-full match)] + [var-name (re-match-group match 1)] + [arg (re-match-group match 2)] + [match-start (re-match-start match)] + [match-end (re-match-end match)] + [var-rel (or (string-find-substring-from full var-name 0) + 0)] + [var-start (+ match-start var-rel)] + [var-end (+ var-start (string-length var-name))] + [binding (make-regex-capture-binding "RES" var-name source + var-start var-end)]) + (and (not (csharp-quoted-argument? arg)) + (csharp-binding-has-type? binding source "HttpResponse") + (finding-for-range-with-bindings rule path source match-start match-end + (list (cons "RES" binding)))))) + (def (scan-csharp-response-write-rule rule path source) + (and (csharp-response-write-rule? rule) + (let ([rx (re csharp-response-write-regex)] + [len (string-length source)]) + (let loop ([start 0] [acc '()]) + (if (> start len) + (nonempty-findings (reverse acc)) + (let ([match (re-search rx source start)]) + (if match + (let* ([finding (csharp-response-write-finding + rule + path + source + match)] + [next (max (+ (re-match-start match) 1) + (re-match-end match))]) + (loop + next + (if finding (cons finding acc) acc))) + (nonempty-findings (reverse acc))))))))) (def (terraform-line-trimmed-end source line-start line-end) (let ([last (skip-horizontal-backward source @@ -9610,6 +9666,26 @@ (cons array-type acc)))) '())) '()))) + (def (simple-declaration-types-before-binding + binding + source) + (let ([name (simple-binding-identifier binding)]) + (if name + (let* ([limit (min (metavariable-binding-start-byte binding) + (string-length source))] + [prefix (substring source 0 limit)] + [pattern (string-append "(^|[^A-Za-z0-9_$])" + "([A-Za-z_$][A-Za-z0-9_.$<>]*)" "[ \\t]+" + (regex-escape-string name) + "[ \\t]*(?:=|;|,|\\))")]) + (regex-fold-matches + pattern + prefix + (lambda (match acc) + (let ([type-name (re-match-group match 2)]) + (if (member type-name acc) acc (cons type-name acc)))) + '())) + '()))) (def (inferred-binding-types binding) (let* ([text (string-trim (metavariable-binding-text binding))] @@ -9660,6 +9736,9 @@ (annotation-types-before-binding binding source) + (simple-declaration-types-before-binding + binding + source) (c-array-declaration-types-before-binding binding source))]) @@ -13413,6 +13492,8 @@ (string=? language "python3") (string=? language "py")) (scan-python-open-redirect-rule rule path source)) + (and (csharp-language? language) + (scan-csharp-response-write-rule rule path source)) (and (yaml-language? language) (scan-yaml-patterns-rule rule path source)) (and (terraform-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" . "F7094F3E0E0AB987") + ("src/semgrep/lang.ss" . "6789CD49CCBB1B74") ("src/semgrep/parse/parse-target.ss" . "E74854DDDACF6BA") - ("src/semgrep/scan.ss" . "CABE02B78971F847") + ("src/semgrep/scan.ss" . "B3CE7574F2A6025E") ("src/semgrep/output/text.ss" . "BE476CB84B807FBA") ("src/semgrep/fix.ss" . "2E5B65B1FEF3B2B1") ("src/semgrep/schema/lang.ss" . "CAE2CA859C9A9FD0") ("src/semgrep/rule.ss" . "E12C108153C181FA") ("src/semgrep/match/structural.ss" . "F7B63A9A6FA028B") ("src/semgrep/main.ss" . "A4EC9E7F2A09D25E") - ("src/semgrep/cli.ss" . "EAC6A60F390262DD")) + ("src/semgrep/cli.ss" . "20EB1B6E083E3DC3")) --- 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 targets.\n")) + (display "MVP support: YAML configs with pattern/pattern-regex rules; json/python/javascript/dockerfile/html/xml/yaml/c/terraform/php/csharp targets.\n")) (def (parse-args args) (let loop ([xs args] --- a/src/semgrep/lang.ss +++ b/src/semgrep/lang.ss @@ -13,7 +13,7 @@ '("json" "python" "js" "ts")) (def text-supported-language-ids - '("dockerfile" "html" "xml" "yaml" "c" "terraform" "php")) + '("dockerfile" "html" "xml" "yaml" "c" "terraform" "php" "csharp")) (def (string-member? needle xs) (and (not (null? xs)) --- a/src/semgrep/scan.ss +++ b/src/semgrep/scan.ss @@ -808,12 +808,17 @@ (string=? canonical "yaml") (string=? canonical "c") (string=? canonical "terraform") - (string=? canonical "php")))) + (string=? canonical "php") + (string=? canonical "csharp")))) (def (c-language? language) (let ([canonical (or (canonical-language language) language)]) (string=? canonical "c"))) +(def (csharp-language? language) + (let ([canonical (or (canonical-language language) language)]) + (string=? canonical "csharp"))) + (def (terraform-language? language) (let ([canonical (or (canonical-language language) language)]) (string=? canonical "terraform"))) @@ -8743,9 +8748,70 @@ path source match) - acc)))) + acc)))) (reverse acc)))))))) +(def csharp-response-write-regex + "\\b([A-Za-z_][A-Za-z0-9_]*)[ \\t]*\\.[ \\t]*Write[ \\t]*\\(([^\\n)]*)\\)") + +(def (csharp-response-write-rule? rule) + (and (string=? (rule-id rule) "no-direct-response-write") + (eq? (rule-pattern-kind rule) 'patterns))) + +(def (csharp-quoted-argument? arg) + (let* ([trimmed (string-trim arg)] + [len (string-length trimmed)]) + (and (> len 0) + (let ([ch (string-ref trimmed 0)]) + (or (char=? ch #\") (char=? ch #\')))))) + +(def (csharp-binding-has-type? binding source expected) + (any? (lambda (actual) (type-name-compatible? expected actual)) + (simple-declaration-types-before-binding binding source))) + +(def (csharp-response-write-finding rule path source match) + (let* ([full (re-match-full match)] + [var-name (re-match-group match 1)] + [arg (re-match-group match 2)] + [match-start (re-match-start match)] + [match-end (re-match-end match)] + [var-rel (or (string-find-substring-from full var-name 0) 0)] + [var-start (+ match-start var-rel)] + [var-end (+ var-start (string-length var-name))] + [binding (make-regex-capture-binding + "RES" + var-name + source + var-start + var-end)]) + (and (not (csharp-quoted-argument? arg)) + (csharp-binding-has-type? binding source "HttpResponse") + (finding-for-range-with-bindings + rule + path + source + match-start + match-end + (list (cons "RES" binding)))))) + +(def (scan-csharp-response-write-rule rule path source) + (and (csharp-response-write-rule? rule) + (let ([rx (re csharp-response-write-regex)] + [len (string-length source)]) + (let loop ([start 0] [acc '()]) + (if (> start len) + (nonempty-findings (reverse acc)) + (let ([match (re-search rx source start)]) + (if match + (let* ([finding + (csharp-response-write-finding + rule path source match)] + [next (max (+ (re-match-start match) 1) + (re-match-end match))]) + (loop next + (if finding (cons finding acc) acc))) + (nonempty-findings (reverse acc))))))))) + (def (terraform-line-trimmed-end source line-start line-end) (let ([last (skip-horizontal-backward source (- line-end 1))]) (if (< last line-start) line-start (+ last 1)))) @@ -10002,6 +10068,29 @@ '())) '()))) +(def (simple-declaration-types-before-binding binding source) + (let ([name (simple-binding-identifier binding)]) + (if name + (let* ([limit (min (metavariable-binding-start-byte binding) + (string-length source))] + [prefix (substring source 0 limit)] + [pattern (string-append + "(^|[^A-Za-z0-9_$])" + "([A-Za-z_$][A-Za-z0-9_.$<>]*)" + "[ \\t]+" + (regex-escape-string name) + "[ \\t]*(?:=|;|,|\\))")]) + (regex-fold-matches + pattern + prefix + (lambda (match acc) + (let ([type-name (re-match-group match 2)]) + (if (member type-name acc) + acc + (cons type-name acc)))) + '())) + '()))) + (def (inferred-binding-types binding) (let* ([text (string-trim (metavariable-binding-text binding))] [len (string-length text)]) @@ -10045,6 +10134,9 @@ (annotation-types-before-binding binding source) + (simple-declaration-types-before-binding + binding + source) (c-array-declaration-types-before-binding binding source))]) @@ -13990,6 +14082,8 @@ (string=? language "python3") (string=? language "py")) (scan-python-open-redirect-rule rule path source)) + (and (csharp-language? language) + (scan-csharp-response-write-rule rule path source)) (and (yaml-language? language) (scan-yaml-patterns-rule rule path source)) (and (terraform-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; do + for ext in py js json txt generic gem ts tsx dockerfile html xml yl test.yaml ejs mustache c tf tpl cs; do candidate="$RULE_DIR/$base.$ext" if [[ -f "$candidate" ]]; then if [[ -L "$candidate" ]]; then --- a/tests/smoke.ss +++ b/tests/smoke.ss @@ -786,6 +786,19 @@ (finding-end-offset (car findings))) => "int result = -i_a;"))) +(test-case "scan C# response write metavariable type" + (let* ([cs-config + "rules:\n - id: no-direct-response-write\n languages: [csharp]\n message: direct response\n severity: WARNING\n patterns:\n - pattern: $RES.Write(...)\n - pattern-not: $RES.Write(\"...\")\n - metavariable-type:\n metavariable: $RES\n type: HttpResponse\n"] + [source + "public void ProcessRequest(HttpContext context) {\n HttpResponse response = context.Response;\n HttpRequest request = context.Request;\n response.Write(\"Hash Test java.security.MessageDigest.getInstance(java.lang.String) executed\");\n response.Write(request.Form[\"input\"]);\n SafeWriter sWriter = response.getSafeWriter();\n sWriter.Write(request.Form[\"input\"]);\n}\n"] + [findings + (scan-config-string cs-config "csharp" "demo.cs" source)]) + (check (length findings) => 1) + (check (substring source + (finding-start-offset (car findings)) + (finding-end-offset (car findings))) + => "response.Write(request.Form[\"input\"])"))) + (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"] @@ -2461,6 +2474,7 @@ (check (guess-language-from-path "demo.c") => "c") (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.rb") => #f)) (test-case "generated language registry"