diff --git a/src/frameforge/semantic.py b/src/frameforge/semantic.py index f570774..f07a899 100644 --- a/src/frameforge/semantic.py +++ b/src/frameforge/semantic.py @@ -17,9 +17,22 @@ def evaluate(root, rule): continue # Comment filtering is intentionally conservative; not a language parser. language = rule["language"] - pattern = (r'//[^\n]*|/\*[\s\S]*?\*/' if language == "csharp" - else r'--\[\[[\s\S]*?\]\]|--[^\n]*') - source = re.sub(pattern, lambda m: re.sub(r'[^\n]', ' ', m.group()), source) + if language == "csharp": + pattern = re.compile( + r'(?P@"(?:""|[^"])*"|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\')' + r'|(?P//[^\n]*|/\*[\s\S]*?\*/)' + ) + else: + pattern = re.compile( + r'(?P\[(?P=*)\[[\s\S]*?\](?P=eq)\])' + r'|(?P"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\')' + r'|(?P--\[(?P=*)\[[\s\S]*?\](?P=comment_eq)\]|--[^\n]*)' + ) + source = pattern.sub( + lambda match: re.sub(r'[^\n]', ' ', match.group()) + if match.group("comment") else match.group(), + source, + ) sources.append((relative, source)) if not sources: diff --git a/tests/test_semantic.py b/tests/test_semantic.py index f9cc3fb..d33487e 100644 --- a/tests/test_semantic.py +++ b/tests/test_semantic.py @@ -22,6 +22,31 @@ def test_positive_negative_and_comments(self): f.write_text("Event += OnEvent;\nEvent -= OnEvent;") self.assertEqual(evaluate(root,rule())["status"],"pattern_observed_control_flow_unverified") + def test_csharp_comment_markers_inside_strings_do_not_hide_code(self): + with tempfile.TemporaryDirectory() as d: + root = Path(d) + f = root/"Root.cs" + f.write_text('var url = "https://example.com"; Event += OnEvent; // Event -= OnEvent;\n') + result = evaluate(root, rule(["Root.cs"])) + self.assertEqual(result["status"], "review_needed") + self.assertEqual([item["token"] for item in result["evidence"]], ["Event += OnEvent;"]) + self.assertEqual(result["missing_tokens"], ["Event -= OnEvent;"]) + + def test_lua_comment_markers_inside_strings_do_not_hide_code(self): + with tempfile.TemporaryDirectory() as d: + root = Path(d) + f = root/"transaction.lua" + f.write_text('local marker = "-- not a comment"; query(db, "COMMIT") -- query(db, "ROLLBACK")\n') + value = { + "plugin_id": "skynet", "id": "transaction", "topic": "transaction", + "recommendation": "review recovery", "language": "lua", + "paths": ["transaction.lua"], "tokens": ['"COMMIT"', '"ROLLBACK"'], + } + result = evaluate(root, value) + self.assertEqual(result["status"], "review_needed") + self.assertEqual([item["token"] for item in result["evidence"]], ['"COMMIT"']) + self.assertEqual(result["missing_tokens"], ['"ROLLBACK"']) + def test_relocation_and_absence(self): with tempfile.TemporaryDirectory() as d: root=Path(d)