Skip to content

Add HTML security rules with markup search semantics - #75

Open
juangaitanv wants to merge 1 commit into
mainfrom
extract-html-security-rules
Open

Add HTML security rules with markup search semantics#75
juangaitanv wants to merge 1 commit into
mainfrom
extract-html-security-rules

Conversation

@juangaitanv

Copy link
Copy Markdown
Contributor

Stacked on #74. This branch sits on fix-html-taint-regression (af1443e) and must merge after it. A diff against main will also show #74's taint fix; the 12 files below are this PR's own.

What this adds

12 HTML security rule IDs in a new rules/html/html_security.ron, plus a minimal AST-vs-Markup boundary so HTML and Django match search-mode rules against node text rather than resolved function names.

  • src/language.rs — new SearchSemantics { Ast, Markup }; LanguageSupport::search_semantics() defaults to Ast; HTML and Django override to Markup.
  • src/scanner/scanning_logic.rs — markup prefiltering and final matching against node text, line attribution to the first positive match, and tightest-span dedup for nested markup findings.
  • src/scanner/conditions.rsnode_kind condition arm.
  • src/rules.rsfirst_positive_match_range for line attribution.
  • tests/strictness/html_security.rs (new), registered in tests/strictness/main.rs.
  • tests/strictness/helpers.rs / language_coverage.rsrun_cli_raw, run_cli_json, sighthound_binary promoted from private-in-language_coverage to pub in helpers so the new suite can drive the CLI. Pure relocation; no coverage lost.

Why it depends on #74

Before #74, src/scanner/modes.rs hard-errored when a language had zero taint-mode rules. HTML has none, so every HTML scan exited 1 with zero findings.

Verified rather than assumed: revert modes.rs to its pre-#74 state and html_security_rules_fire_in_default_mode fails with Error: No taint flow rules found. With #74 in the base it passes. This PR adds no hunk to modes.rs.

Review notes

  • Shared surface with fix: skip taint pass instead of erroring when a language has no taint rules #74: tests/strictness/helpers.rs and tests/strictness/language_coverage.rs. If fix: skip taint pass instead of erroring when a language has no taint rules #74 is amended or force-pushed, the helper move here needs re-deriving.
  • include_dir! staleness: rules are embedded at compile time via include_dir! in src/rules.rs, and there is no build.rs. Editing a .ron with a warm target/ yields zero findings until you touch src/rules.rs. Flagging it so the next person loses less time than I did.
  • Pre-existing gap: call_node_types() omits self_closing_tag, so rules do not fire on self-closing elements. Not introduced here, not fixed here.
  • Dead code: "element" in call_node_types() never produces findings — get_function_name resolves tag_name via CommonUtils::find_child, which walks direct children only, and per tree-sitter-html's node-types an element has no direct tag_name.

Not covered by tests

  • Django running Markup. The override exists; no test exercises it.
  • The .expect() in the markup line-attribution block. Unreachable by construction (markup == true implies node_text.is_some()), but unasserted.
  • The get_finding_type()"vulnerability" fallback.

Relationship to #9

#9 carries the same 12 rule IDs from the same lineage; this PR supersedes that portion. #9's sql/xml/properties/config rules, its parser-free text-scanning engine, and its unless exclusion mechanism are not included here — all were on this chunk's exclusion list. #9 stays open for that work.

Verification

  • cargo test --test strictness_tests — 47 passed
  • make test-unit — 261 passed
  • cargo fmt --all -- --check — clean
  • Fusion contract (sighthound --output-format json <path>): positive fixture → exit 0, 12 findings at lines 7, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, empty stderr; safe fixture → []
  • --taint-analysis --output-format json → stdout is [], notice routed to stderr via ui::warn

Ports 12 HTML security rule IDs into rules/html/html_security.ron and
introduces a SearchSemantics abstraction (src/language.rs) so HTML and
Django scanning can use markup-aware prefiltering and final matching
instead of taint-rule search, avoiding false negatives when a language
has zero taint rules. Wires the new semantics through the scanner's
prefilter, match, and dedup paths (scanning_logic.rs, conditions.rs,
rules.rs) and adds strictness/unit coverage plus positive and safe
HTML fixtures proving the rules fire in default mode without
regressing existing suites.
@juangaitanv juangaitanv mentioned this pull request Aug 25, 2026
Base automatically changed from fix-html-taint-regression to main August 25, 2026 21:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant