Skip to content

Commit 9799f1f

Browse files
committed
next-line fix scanner improvements
1 parent 3a22d62 commit 9799f1f

3 files changed

Lines changed: 37 additions & 28 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "oxide-ci"
3-
version = "0.2.9"
3+
version = "0.2.10"
44
edition = "2024"
55

66
[dependencies]

src/modules/sast.rs

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,37 @@ use std::path::{Path, PathBuf};
1111
use streaming_iterator::StreamingIterator;
1212
use tree_sitter::{Language, Parser, Query, QueryCursor};
1313

14+
// ── Inline suppression ────────────────────────────────────────────────────────
15+
16+
/// Return `true` if the finding at `row` (0-based) is suppressed.
17+
///
18+
/// Checks the matched line itself AND the line immediately after it.
19+
/// Formatters (Prettier, ESLint) often move `// oxide-ci: ignore` comments
20+
/// inside object literals to the next line, e.g.:
21+
/// ```
22+
/// dangerouslySetInnerHTML={{ ← flagged line (row N)
23+
/// // oxide-ci: ignore ← comment on row N+1
24+
/// __html: sanitized,
25+
/// }}
26+
/// ```
27+
fn is_suppressed(source: &[u8], row: usize) -> bool {
28+
let mut lines = source.split(|&b| b == b'\n');
29+
// Collect the target line and the one after it.
30+
for (i, line) in lines.by_ref().enumerate() {
31+
if (i == row || i == row + 1)
32+
&& std::str::from_utf8(line)
33+
.map(|l| l.contains("oxide-ci: ignore"))
34+
.unwrap_or(false)
35+
{
36+
return true;
37+
}
38+
if i > row + 1 {
39+
break;
40+
}
41+
}
42+
false
43+
}
44+
1445
// ── Language dispatch ─────────────────────────────────────────────────────────
1546

1647
fn language_for(path: &Path) -> Option<Language> {
@@ -94,12 +125,7 @@ fn scan_string_literals(
94125
let line_no = node.start_position().row + 1; // 1-based
95126

96127
// Inline suppression on the source line
97-
let source_line = source
98-
.split(|&b| b == b'\n')
99-
.nth(node.start_position().row)
100-
.and_then(|l| std::str::from_utf8(l).ok())
101-
.unwrap_or("");
102-
if source_line.contains("oxide-ci: ignore") {
128+
if is_suppressed(source, node.start_position().row) {
103129
continue;
104130
}
105131

@@ -589,12 +615,7 @@ fn scan_dangerous_patterns(
589615
if let Some(cap) = m.captures.iter().find(|c| c.index == match_idx) {
590616
let line_no = cap.node.start_position().row + 1;
591617

592-
let source_line = source
593-
.split(|&b| b == b'\n')
594-
.nth(cap.node.start_position().row)
595-
.and_then(|l| std::str::from_utf8(l).ok())
596-
.unwrap_or("");
597-
if source_line.contains("oxide-ci: ignore") {
618+
if is_suppressed(source, cap.node.start_position().row) {
598619
continue;
599620
}
600621

@@ -723,12 +744,7 @@ fn scan_dangerous_patterns(
723744
if let Some(cap) = m.captures.iter().find(|c| c.index == match_idx) {
724745
let line_no = cap.node.start_position().row + 1;
725746

726-
let source_line = source
727-
.split(|&b| b == b'\n')
728-
.nth(cap.node.start_position().row)
729-
.and_then(|l| std::str::from_utf8(l).ok())
730-
.unwrap_or("");
731-
if source_line.contains("oxide-ci: ignore") {
747+
if is_suppressed(source, cap.node.start_position().row) {
732748
continue;
733749
}
734750

@@ -809,14 +825,7 @@ fn scan_complexity(
809825
let line_no = node.start_position().row + 1;
810826

811827
// Inline suppression
812-
let suppressed = source
813-
.split(|&b| b == b'\n')
814-
.nth(node.start_position().row)
815-
.and_then(|l| std::str::from_utf8(l).ok())
816-
.map(|l| l.contains("oxide-ci: ignore"))
817-
.unwrap_or(false);
818-
819-
if !suppressed {
828+
if !is_suppressed(source, node.start_position().row) {
820829
// SMELL/LongFunction
821830
if !sast_config
822831
.disabled_rules

0 commit comments

Comments
 (0)