feat(static): classify suspicious URLs in configuration files - #134
Conversation
…et/localtunnel.me ngrok migrated off ngrok.io to ngrok-free.dev / ngrok.app, so exfil to a current ngrok endpoint slipped past the suspicious-domain checks. Add the modern ngrok domains plus bore.pub, serveo.net and localtunnel.me to: - ContextExtractor.SUSPICIOUS_DOMAINS (Python string-literal URL classification) - tool_chaining_abuse_generic.yara / command_injection_generic.yara exfil dests - cross_skill_scanner exfil pattern list Add regression tests for the new domains (YARA true-positives + ContextExtractor). Co-authored-by: Cursor <cursoragent@cursor.com>
The suspicious/legitimate domain lists and matching logic previously lived inside ContextExtractor and only ran over Python AST string literals, so a tunnel/proxy endpoint hidden in a config value (e.g. base_url in config.yaml) was never classified. Refactor (behavior-preserving): extract SUSPICIOUS_DOMAINS/LEGITIMATE_DOMAINS and the classification into skill_scanner/core/static_analysis/url_classifier.py (classify_url + extract_urls). ContextExtractor imports from it and keeps the lists as class attributes for backward compatibility; existing suspicious-URL behavior is unchanged (covered by refactor-safety tests). New pass: StaticAnalyzer._scan_config_files() parses config.yaml/.yml/.json, settings.*, and *.toml (regex fallback on parse failure), runs each URL through the shared classifier, and emits CONFIG_SUSPICIOUS_URL (HIGH). Registered in the core pack.yaml. Docs updated (static-analyzer, behavioral-analyzer). Adds unit tests plus a labeled config-routed exfil eval sample; benchmark stays at 100% P/R. Co-authored-by: Cursor <cursoragent@cursor.com>
Collapse the structured YAML/JSON/TOML parse-and-walk in config URL scanning down to a single raw-text extract_urls() pass. The raw scan is simpler, drops the json/yaml/tomllib imports and the _iter_string_values recursion, and additionally catches suspicious URLs hidden in config comments. classify_url() still only flags known tunnel/exfil domains, so false-positive risk stays low. Co-authored-by: Cursor <cursoragent@cursor.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe change adds shared URL classification and configuration-file URL scanning to static analysis. It registers ChangesConfig URL scanning and tunnel domain detection
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant StaticAnalyzer
participant ConfigFile
participant url_classifier
participant Finding
StaticAnalyzer->>ConfigFile: read recognized configuration content
StaticAnalyzer->>url_classifier: extract and classify URLs
url_classifier-->>StaticAnalyzer: return URL classifications
StaticAnalyzer->>Finding: emit redacted CONFIG_SUSPICIOUS_URL finding
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@skill_scanner/core/analyzers/static.py`:
- Around line 627-638: The finding construction in static.py is copying the raw
config URL into multiple report fields, which can leak credentials or other
sensitive URL parts. Update the suspicious/tunnel URL finding path to use a
redaction helper near _find_line_number (for example, _redact_url_for_finding)
that preserves scheme/host/path but strips userinfo and replaces query/fragment
with placeholders. Apply the redacted value consistently wherever the URL is
assigned to snippet, metadata["url"], or any other finding payload in this
analyzer.
In `@skill_scanner/core/static_analysis/url_classifier.py`:
- Around line 145-158: The URL classifier in classify_url is matching
LEGITIMATE_DOMAINS and SUSPICIOUS_DOMAINS against the entire URL string, which
allows path/query substrings to override the host. Update classify_url to parse
the URL, normalize the hostname, and make decisions using exact host or
subdomain boundary checks against the hostname only. Keep the existing
precedence behavior, but apply it after hostname-based matching rather than raw
substring checks, and use the existing URL_RE/_URL_RE helpers and domain lists
to locate the logic.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2c59b18e-dcf2-4aa3-b91d-21fa3c5ee68a
📒 Files selected for processing (15)
docs/architecture/analyzers/behavioral-analyzer.mddocs/architecture/analyzers/static-analyzer.mdevals/skills/data-exfiltration/config-tunnel-exfil/SKILL.mdevals/skills/data-exfiltration/config-tunnel-exfil/_expected.jsonevals/skills/data-exfiltration/config-tunnel-exfil/config.yamlskill_scanner/core/analyzers/cross_skill_scanner.pyskill_scanner/core/analyzers/static.pyskill_scanner/core/static_analysis/context_extractor.pyskill_scanner/core/static_analysis/url_classifier.pyskill_scanner/data/packs/core/pack.yamlskill_scanner/data/packs/core/yara/command_injection_generic.yaraskill_scanner/data/packs/core/yara/tool_chaining_abuse_generic.yaratests/static_analysis/test_config_url_scanning.pytests/static_analysis/test_suspicious_url_domains.pytests/test_yara_true_positives.py
ProviderConfig.validate() required a truthy credential for every provider except Bedrock and Ollama, and the only credential source it checked for Vertex was GOOGLE_APPLICATION_CREDENTIALS. This blocked ambient auth via a GCE/Cloud Run attached service account or Workload Identity, even though LiteLLM/google-auth already fall back to it automatically when no explicit credential is passed -- the same pattern already supported for Bedrock's IAM role. Excludes is_vertex from the check, mirroring the Bedrock/Ollama precedent, and documents the fallback.
- _resolve_api_key() now returns None for Vertex instead of the GOOGLE_APPLICATION_CREDENTIALS path, since vertex_ai/gemini-* models set both is_vertex and is_gemini, which was causing the file path to be written into GEMINI_API_KEY. - Regenerated configuration-reference.md via generate_reference_docs.py instead of hand-editing, and updated the underlying descriptions so the doc doesn't drift on next regeneration. Addresses CodeRabbit review feedbak on cisco-ai-defense#144.
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
# Conflicts: # skill_scanner/core/static_analysis/context_extractor.py
# Conflicts: # skill_scanner/core/static_analysis/context_extractor.py
Summary
Suspicious/tunnel URLs were only classified when they appeared as Python string literals (via
ContextExtractor). Config files (config.yaml,settings.toml, …) are typedotherand never reached that path, so a tunnel/exfil endpoint hidden in a config value went unnoticed.This PR:
ContextExtractorinto a sharedcore/static_analysis/url_classifier.py(classify_url,extract_urls) — a single source of truth reused by every analyzer._scan_config_files()to the static analyzer, emittingCONFIG_SUSPICIOUS_URL(HIGH) for suspicious URLs found in config files. URLs are extracted from the raw file text, so endpoints in comments are covered too.CONFIG_SUSPICIOUS_URLindata/packs/core/pack.yaml; updates static/behavioral analyzer docs; adds an eval sample (config-tunnel-exfil).Test plan
tests/static_analysis/test_config_url_scanning.py— classifier unit tests, refactor-safety tests (ContextExtractor behavior unchanged), and config-format integration tests (YAML/JSON/TOML).uv run pytest tests/passes.uv run pre-commit run --all-filespasses.uv run python evals/runners/benchmark_runner.py— 100% precision/recall, no regression.Made with Cursor
Summary by CodeRabbit
New Features
Documentation
Tests