Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions scripts/codelens.py
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,8 @@ def main():
global_max_tokens = None
global_lite = False
global_deep = False
global_disable_suppression = False
global_ignore_pattern = None

i = 1
while i < len(sys.argv):
Expand Down Expand Up @@ -878,10 +880,22 @@ def main():
global_lite = True
elif arg == '--deep':
global_deep = True
elif arg == '--disable-suppression':
global_disable_suppression = True
elif arg == '--codelens-ignore-pattern' and i + 1 < len(sys.argv):
global_ignore_pattern = sys.argv[i + 1]
elif arg.startswith('--codelens-ignore-pattern='):
global_ignore_pattern = arg.split('=', 1)[1]
i += 1

args = parser.parse_args()

# Apply global flags to args
if getattr(args, 'disable_suppression', None) is None:
args.disable_suppression = globals().get('global_disable_suppression', False)
if getattr(args, 'codelens_ignore_pattern', None) is None:
args.codelens_ignore_pattern = globals().get('global_ignore_pattern', None)

# Resolve format: subparser --format overrides global --format
# If neither is set, use the parser's default (which may be "ai" if CODELENS_AI_MODE=1)
subparser_format = getattr(args, 'format', None)
Expand Down Expand Up @@ -1126,6 +1140,62 @@ def main():
if args.command == "ask" and isinstance(result, dict):
format_command = result.get("query_interpretation", {}).get("interpreted_as", "ask")

# ─── Apply inline suppressions ──
if not getattr(args, 'disable_suppression', False) and isinstance(result, dict):
try:
from suppression import apply_suppressions, update_stats_with_suppressions, DEFAULT_KEYWORD_PATTERN

keyword_pattern = getattr(args, 'codelens_ignore_pattern', None) or DEFAULT_KEYWORD_PATTERN

# Collect source files from the result
source_files = {}
finding_keys = ("findings", "leaks", "hints", "issues", "violations", "matches", "chains")
for key in finding_keys:
val = result.get(key)
if isinstance(val, list):
for f in val:
if isinstance(f, dict):
fp = f.get("file") or f.get("defined_in") or ""
if fp and fp not in source_files:
try:
with open(fp, 'r', encoding='utf-8', errors='replace') as fh:
source_files[fp] = fh.read()
except (IOError, OSError):
pass
elif isinstance(val, dict):
for sub_val in val.values():
if isinstance(sub_val, list):
for f in sub_val:
if isinstance(f, dict):
fp = f.get("file") or f.get("defined_in") or ""
if fp and fp not in source_files:
try:
with open(fp, 'r', encoding='utf-8', errors='replace') as fh:
source_files[fp] = fh.read()
except (IOError, OSError):
pass

if source_files:
# Collect all findings from result
all_findings = []
for key in finding_keys:
val = result.get(key)
if isinstance(val, list):
all_findings.extend(val)
elif isinstance(val, dict):
for sub_val in val.values():
if isinstance(sub_val, list):
all_findings.extend(sub_val)

if all_findings:
apply_suppressions(all_findings, source_files, keyword_pattern=keyword_pattern)
update_stats_with_suppressions(result)

except ImportError:
pass # suppression module not available
except Exception as e:
logger.warning(f"Suppression processing failed: {e}", exc_info=True)

# ─── Format and print output ──
print(format_output(result, args.format, format_command, workspace))

Expand Down
24 changes: 24 additions & 0 deletions scripts/formatters/sarif.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,30 @@ def _build_results(command: str, findings: List[Dict], workspace: str) -> List[D
if finding.get("category"):
result["properties"]["category"] = finding["category"]

# ─── Add suppressions field per SARIF spec ──────────────────────
# https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html#_Toc34317632
if finding.get("suppressed"):
suppressions = []
reason = finding.get("suppressed_reason", "")
suppressed_rules = finding.get("suppressed_rules", [])

if suppressed_rules:
# Specific rules suppressed
for rid in suppressed_rules:
suppressions.append({
"kind": "inSource",
"justification": reason or f"Suppressed via inline annotation for rule: {rid}",
})
else:
# All rules suppressed on this line
suppressions.append({
"kind": "inSource",
"justification": reason or "Suppressed via inline annotation (all rules)",
})

result["suppressions"] = suppressions
result["kind"] = "informational"

# Add related locations for taint analysis
if finding.get("taint_path") and finding.get("source"):
source_loc = {
Expand Down
Loading
Loading