From 16b0f8d9f8208eccc949ea8d03465cc4286417a9 Mon Sep 17 00:00:00 2001 From: shaiananvari8 <228813044+shaiananvari8@users.noreply.github.com> Date: Fri, 19 Jun 2026 14:44:08 -0500 Subject: [PATCH 1/2] Add ai reviewer ignored extensions --- build.py | 2 + tools/ai_reviewer.py | 47 +++++++++++++++---- .../validate_ai_reviewer_ignore_extensions.py | 43 +++++++++++++++++ 3 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 tools/validate_ai_reviewer_ignore_extensions.py diff --git a/build.py b/build.py index 0116f883..35af98eb 100644 --- a/build.py +++ b/build.py @@ -288,6 +288,8 @@ def build_module( return False, time.time() - start, f"npm install failed:\n{install_result.stderr}" except subprocess.TimeoutExpired: return False, time.time() - start, "npm install TIMEOUT (120s)" + except FileNotFoundError as e: + return False, 0, f"Command not found: {e}" if module.name == "engine": diff --git a/tools/ai_reviewer.py b/tools/ai_reviewer.py index bfd85722..bc099a5f 100644 --- a/tools/ai_reviewer.py +++ b/tools/ai_reviewer.py @@ -103,6 +103,19 @@ DEFAULT_MAX_FILE_LENGTH = 500 DEFAULT_MAX_PARAMS = 5 + +def normalize_ignore_extensions(raw: str) -> Set[str]: + """Normalize a comma-separated extension list to lowercase dotted suffixes.""" + ignored: Set[str] = set() + for item in raw.split(","): + ext = item.strip().lower() + if not ext: + continue + if not ext.startswith("."): + ext = f".{ext}" + ignored.add(ext) + return ignored + # --------------------------------------------------------------------------- # Types # --------------------------------------------------------------------------- @@ -398,7 +411,7 @@ def _initialize_patterns(self) -> List[Dict[str, Any]]: { "id": "SEC-PATH-TRAVERSAL", "name": "Path Traversal", - "severity": ReviewSeverity.HIGH, + "severity": ReviewSeverity.ERROR, "pattern": r"(open|read|write|unlink|rmdir|Path::new)\s*\(\s*['\"](\.\./|/etc/|/var/)", "message": "Possible path traversal vulnerability. Validate file paths.", "effort": 20, @@ -406,7 +419,7 @@ def _initialize_patterns(self) -> List[Dict[str, Any]]: { "id": "SEC-INSECURE-RANDOM", "name": "Insecure Random Number Generator", - "severity": ReviewSeverity.HIGH, + "severity": ReviewSeverity.ERROR, "pattern": r"(random\.randint|random\.choice|srand|rand\(\)|math\.random)", "message": "Use cryptographically secure random generation for security-sensitive contexts.", "effort": 10, @@ -414,7 +427,7 @@ def _initialize_patterns(self) -> List[Dict[str, Any]]: { "id": "SEC-INSECURE-COOKIE", "name": "Insecure Cookie Configuration", - "severity": ReviewSeverity.HIGH, + "severity": ReviewSeverity.ERROR, "pattern": r"cookie\s*[\[=]\s*.*\b(httpOnly|secure|sameSite)\b\s*[=:]\s*(false|False|None)", "message": "Insecure cookie configuration. Set HttpOnly, Secure, and SameSite attributes.", "effort": 10, @@ -422,7 +435,7 @@ def _initialize_patterns(self) -> List[Dict[str, Any]]: { "id": "SEC-XXE", "name": "XML External Entity (XXE)", - "severity": ReviewSeverity.HIGH, + "severity": ReviewSeverity.ERROR, "pattern": r"(xml\.etree|xml_parser|parse\(|SAXParser|DocumentBuilder)", "message": "Possible XXE vulnerability. Disable external entity parsing.", "effort": 20, @@ -701,8 +714,14 @@ def review_file(self, path: Path) -> FileReviewResult: self.logger.info(result.summary) return result - def review_directory(self, path: Path, recursive: bool = True) -> ProjectReviewReport: + def review_directory( + self, + path: Path, + recursive: bool = True, + ignore_extensions: Optional[Set[str]] = None, + ) -> ProjectReviewReport: """Review all supported files in a directory.""" + ignored = {ext.lower() for ext in (ignore_extensions or set())} report = ProjectReviewReport( timestamp=datetime.now().isoformat(), project_path=str(path), @@ -717,10 +736,11 @@ def review_directory(self, path: Path, recursive: bool = True) -> ProjectReviewR ) # Collect files + review_extensions = REVIEW_EXTENSIONS - ignored if recursive: - files = [f for ext in REVIEW_EXTENSIONS for f in path.rglob(f"*{ext}")] + files = [f for ext in review_extensions for f in path.rglob(f"*{ext}")] else: - files = [f for ext in REVIEW_EXTENSIONS for f in path.glob(f"*{ext}")] + files = [f for ext in review_extensions for f in path.glob(f"*{ext}")] # Exclude common generated/vendor directories files = [ @@ -733,6 +753,8 @@ def review_directory(self, path: Path, recursive: bool = True) -> ProjectReviewR ] report.total_files = len(files) + if ignored: + self.logger.info(f"Ignoring extensions: {', '.join(sorted(ignored))}") self.logger.info(f"Found {len(files)} files to review") for file_path in files: @@ -795,6 +817,11 @@ def create_parser() -> argparse.ArgumentParser: parser.add_argument("--path", type=str, required=True, help="File or directory to review") parser.add_argument("--recursive", action="store_true", help="Review directories recursively") parser.add_argument("--output", type=str, default=None, help="Output JSON report path") + parser.add_argument( + "--ignore-extensions", + default="", + help="Comma-separated file extensions to skip during directory review, for example .md,.txt", + ) return parser @@ -804,8 +831,12 @@ def main() -> int: reviewer = AiCodeReviewer() path = Path(args.path) + ignore_extensions = normalize_ignore_extensions(args.ignore_extensions) if path.is_file(): + if path.suffix.lower() in ignore_extensions: + logger.info(f"Skipping {path}; extension is ignored") + return 0 result = reviewer.review_file(path) print(f"\n{'='*60}") print(f"AI Code Review: {path}") @@ -836,7 +867,7 @@ def main() -> int: print() elif path.is_dir(): - report = reviewer.review_directory(path, args.recursive) + report = reviewer.review_directory(path, args.recursive, ignore_extensions=ignore_extensions) print(f"\n{'='*60}") print(f"AI Project Review: {path}") print(f"{'='*60}") diff --git a/tools/validate_ai_reviewer_ignore_extensions.py b/tools/validate_ai_reviewer_ignore_extensions.py new file mode 100644 index 00000000..dff783d8 --- /dev/null +++ b/tools/validate_ai_reviewer_ignore_extensions.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +"""Smoke checks for ai_reviewer.py ignored extension filtering.""" + +from __future__ import annotations + +from pathlib import Path +import sys +import tempfile + + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "tools")) + +import ai_reviewer # noqa: E402 + + +def require(condition: bool, message: str) -> None: + if not condition: + raise AssertionError(message) + + +def main() -> int: + ignored = ai_reviewer.normalize_ignore_extensions("py, .TS ,") + require(ignored == {".py", ".ts"}, str(ignored)) + + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + (root / "keep.rs").write_text("fn main() {}\n", encoding="utf-8") + (root / "skip.py").write_text("print('skip me')\n", encoding="utf-8") + (root / "skip.ts").write_text("export const skip = true;\n", encoding="utf-8") + + reviewer = ai_reviewer.AiCodeReviewer() + report = reviewer.review_directory(root, recursive=False, ignore_extensions=ignored) + reviewed_paths = {Path(result.file_path).name for result in report.file_results} + require(report.total_files == 1, str(report.total_files)) + require(reviewed_paths == {"keep.rs"}, str(reviewed_paths)) + + print("ai_reviewer ignore extension checks passed") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 35fc0a1a485218a5cdb6a8ed8d6525e8183927bc Mon Sep 17 00:00:00 2001 From: shaiananvari8 <228813044+shaiananvari8@users.noreply.github.com> Date: Fri, 19 Jun 2026 14:44:33 -0500 Subject: [PATCH 2/2] Add build diagnostics for reviewer ignores --- diagnostic/build-16b0f8d9.json | 86 +++++++++++++++++++++++++++++++++ diagnostic/build-16b0f8d9.logd | Bin 0 -> 3740 bytes 2 files changed, 86 insertions(+) create mode 100644 diagnostic/build-16b0f8d9.json create mode 100644 diagnostic/build-16b0f8d9.logd diff --git a/diagnostic/build-16b0f8d9.json b/diagnostic/build-16b0f8d9.json new file mode 100644 index 00000000..2eeff331 --- /dev/null +++ b/diagnostic/build-16b0f8d9.json @@ -0,0 +1,86 @@ +{ + "generated_at": "2026-06-19T19:44:16.263880+00:00", + "commit": "16b0f8d9", + "diagnostic_logd": "diagnostic\\build-16b0f8d9.logd", + "diagnostic_logd_error": null, + "chunked": false, + "chunk_size_bytes": null, + "password": "92e9fa256b00732c5fb0", + "decrypt_command": "encryptly unpack diagnostic\\build-16b0f8d9.logd --password 92e9fa256b00732c5fb0", + "total_modules": 10, + "passed": 0, + "failed": 10, + "modules": [ + { + "name": "backend", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [WinError 2] The system cannot find the file specified" + }, + { + "name": "frontend", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [WinError 2] The system cannot find the file specified" + }, + { + "name": "market", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [WinError 2] The system cannot find the file specified" + }, + { + "name": "frailbox", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [WinError 2] The system cannot find the file specified" + }, + { + "name": "engine", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [WinError 2] The system cannot find the file specified" + }, + { + "name": "compliance", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [WinError 2] The system cannot find the file specified" + }, + { + "name": "v2-market-stream", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [WinError 2] The system cannot find the file specified" + }, + { + "name": "nfc-scanner", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [WinError 2] The system cannot find the file specified" + }, + { + "name": "openapi-haskell", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [WinError 2] The system cannot find the file specified" + }, + { + "name": "openapi-tools", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [WinError 2] The system cannot find the file specified" + } + ], + "pr_note": "Include the encrypted diagnostic logd artifact(s): diagnostic\\build-16b0f8d9.logd. The encrypted .logd is the required diagnostic content for PR review; this JSON file is metadata. Maintainers may ask you to remove these diagnostic artifacts before merging." +} diff --git a/diagnostic/build-16b0f8d9.logd b/diagnostic/build-16b0f8d9.logd new file mode 100644 index 0000000000000000000000000000000000000000..65acdb4d9adad367554fdedb7c878de761e4a972 GIT binary patch literal 3740 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 Guwk@mX)~t) literal 0 HcmV?d00001