Skip to content

feat(scanner)!: modernize core detection - #210

Merged
vineethsai7 merged 12 commits into
mainfrom
feat/core-cel-modernization
Sep 3, 2026
Merged

vineethsai7 merged 12 commits into
mainfrom
feat/core-cel-modernization

Conversation

@vineethsai7

@vineethsai7 vineethsai7 commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR modernizes Skill Scanner's deterministic core and adds a bounded CEL decision layer backed by the official cel-go v0.32.0 runtime.

CEL correlates typed analyzer facts after deterministic detection and before optional LLM/Meta analysis. It gates existing candidates only: it cannot create findings or change rule IDs, categories, or severities. All eight bundled CEL rules remain individually shadow, so this PR does not enable suppression.

Headline metrics are core-only. ATR, LLM, Meta, VirusTotal, and administrator-local packs are excluded.

Changelog

Added

  • Versioned protobuf fact contract: skill_scanner.semantic.v1.ScanFacts.
  • Official cel-go v0.32.0 helper, immutable compiled generations, runtime attestation, expression hashes, fail-open telemetry, and bounded fact projection.
  • CEL policy/CLI modes: off, shadow, and enforce.
  • Strict schema-v2 pack validation, validate-rules, and repeatable administrator-trusted local packs without arbitrary Python loading.
  • Structured Python/JavaScript/TypeScript source/sink metadata and bounded reference/correlation facts.
  • Core candidate extractors for active dynamic execution, remote acquire-and-execute, active semantic directives, Unicode instruction smuggling, hidden HTML instructions, OOXML relationships, and contextual behavior chains.
  • Exact finding-identity evaluation, immutable dataset locks, safe non-executing HF adapters, deterministic comparisons, and promotion/release evidence.
  • Official Codex/Claude Code/Cursor compatibility and NotInject hard-negative benchmarks.
  • Native helper wheel/sdist build, SBOM/dependency evidence, and CPython 3.11-3.14 smoke coverage.

Changed

  • The core pack manifest is authoritative schema v2 for identity, source, category, severity, implementation, and CEL rollout.
  • CEL runs centrally after deterministic analyzers and before optional LLM adjudication.
  • Balanced and strict policies enable correlation with CEL shadow telemetry; permissive keeps correlation disabled and CEL off by default.
  • Evaluation ingestion/scan failures remain in denominators, and golden fixtures use one-to-one rule/category/severity/path/evidence/analyzer identity.
  • Runtime performance gates now apply to aggregate tracks, while subgroup accuracy, false-negative, FPR, fallback, projection, and error gates remain enforced.
  • Release workflows bind privileged work to an immutable verified tag/SHA.

Security

  • Canonical path containment and symlink-boundary validation for API/scanner filesystem access.
  • Private process-owned API upload roots and deny-by-default server path access.
  • Bounded file reads, expression/fact limits, redacted CLI diagnostics, and bounded parsing of attacker-controlled input.
  • Lexical-scope-aware Python eval/exec/os/subprocess aliases and JavaScript eval/child_process aliases, including bounded function, arrow, block, catch, loop, reassignment, semicolonless CommonJS, and template-substitution handling, close review-identified masking and leakage paths without treating inert text or shadowed calls as executable code.
  • Manifest-incomplete projection errors survive CEL field masking. LLM and Meta validation now admit only evidence actually rendered in the current request/batch and enforce request-specific contracts for every provider. Primary analysis validates verdict, severity, and AITech/AISubtech taxonomy and emits an analyzer failure when valid consensus runs cannot form a majority; invalid Meta classifications degrade safely and retain original findings.
  • Quoted workflow inputs and immutable release checkouts.
  • Invalid trusted rules fail initialization; CEL runtime/projection failures fail open and retain findings.

Breaking changes

  • Python 3.10 support is removed. The package now requires CPython >=3.11,<3.15; Python 3.10 consumers must pin the previous release.
  • Wheels are platform-specific because they include the CEL helper.
  • Supported release targets are glibc Linux x86-64/ARM64, macOS x86-64/ARM64, and Windows x86-64. Alpine/musl, PyPy, and Windows ARM remain unsupported.
  • Source builds require the qualified Go toolchain used to compile the CEL helper.
  • Invalid selected rule packs now fail startup rather than silently dropping malformed or inconsistent rules.
  • Administrator-trusted local packs use schema v2. Legacy custom YARA remains available through --custom-rules.
  • Balanced/strict scans now include correlation and CEL shadow metadata, so findings, metadata, and scan time may differ from prior releases.

Architecture and rollout

package
  -> deterministic analyzers
  -> bounded ScanFacts projection
  -> CEL candidate gate
  -> optional LLM/Meta analysis
  -> policy normalization
  -> report
  • true: retain.
  • false in shadow: retain and record would_suppress.
  • false in enforce: suppress only if that rule is also individually promoted.
  • Projection truncation, evaluator failure, non-Boolean output, or circuit breaker: fail open.
  • Parse/type/schema errors in selected trusted packs: fail startup.

All bundled CEL rules remain shadow. The measured quality gain in this PR comes from deterministic core-analyzer improvements, not CEL suppression.

Benchmark results

Dataset: ProtectSkills/MaliciousSkillBench at revision d4b42ce5766a6e0359c987cf59c1007cb3795a90. Configuration: deterministic core only, CEL off/shadow, no ATR/LLM/Meta.

Development train/validation population

Build N TP FP TN FN Precision Recall F1 FPR
origin/main 6,594 1,045 48 1,290 4,211 95.61% 19.88% 32.92% 3.59%
This PR, core + CEL shadow 6,594 1,652 14 1,324 3,604 99.16% 31.43% 47.73% 1.05%
  • F1: +14.81 percentage points (+45.00% relative).
  • Recall: +11.55 points; TP +607.
  • FPR: -2.54 points (-70.83% relative); FP -34.
  • Five shadow runs produced identical findings and telemetry.
  • Each shadow run evaluated 154 candidates with zero would-suppress, suppression, fallback, incomplete-projection, or CEL errors.
  • Worst CEL share was 0.0616%; shadow P95 was +1.50% versus off.

This split is development evidence and was used during tuning.

Locked source-disjoint population

Build N TP FP TN FN Precision Recall F1 FPR
origin/main 1,384 33 20 525 806 62.26% 3.93% 7.40% 3.67%
This PR, core + CEL shadow 1,384 65 42 503 774 60.75% 7.75% 13.74% 7.71%
  • Recall and F1 improve, but FPR regresses by 4.04 points and precision declines slightly.
  • Current precision Wilson 95% CI: 51.28-69.47%; recall: 6.12-9.75%; FPR: 5.75-10.25%.
  • Off/shadow findings were identical; CEL evaluated and retained 25 candidates with zero fallback, projection, or evaluator errors.
  • The historical baseline had 11 scan errors where the current loader records validated recoveries, so the comparison is not methodologically perfect.

This result fails the no-FPR-regression promotion criterion. It does not justify CEL enforcement or a release-pass claim. The split has been observed and is frozen against further tuning.

Compatibility and supplemental evidence

Corpus Population Core + CEL-shadow result Interpretation
Golden corpus 24 23/23 exact findings, package TP=14/TN=10, 0 FP/FN/errors All 17 categories and five severities; five deterministic runs
Official Codex/Claude Code/Cursor skills 111 30 MEDIUM+, 8 HIGH+; no errors Down from 33/11 on the same lock; compatibility/FP mining, not benign gold
NotInject 339 0 actionable; five byte-identical runs Prompt-text hard negatives; diagnostic rate 0%
HarmfulSkillBench 200 7 actionable, 6 HIGH+; one locked quarantine Positive-only supplemental diagnostic; no precision/FPR/F1
OpenSkillRisk 263 76 actionable (28.90%); two host quarantines Positive-only supplemental recall; no precision/FPR/F1

Official-skill off/shadow finding identities were exact. CEL evaluated 36 candidates, recorded 28 would-suppress decisions, and had zero fallbacks/errors; CEL used 0.239% of scan time. The remaining HIGH+ official findings are contextual hard negatives, not proof that the packages are malicious.

Validation

  • Full Python test matrix runs on CPython 3.11-3.14; the exact final-head status is reported by the required GitHub checks.
  • Golden/release/evaluation integrity: 285 focused tests plus 156 comparison/workflow tests passed.
  • CEL: 927 manifest rules, 46 signatures, 8 expressions type-checked; 127/127 precision tests and 220 broader tests passed (20 host-toolchain skips).
  • Ruff check/format, mypy, generated docs, taxonomy, YAML/TOML, actionlint, and pre-commit passed.
  • go test ./... and go vet ./... passed for the v0.32.0 helper.
  • pip-audit: no known vulnerabilities.
  • Wheel installs and validate-rules passed on CPython 3.11, 3.12, 3.13, and 3.14; sdist install passed; Python 3.10 rejection verified.
  • Official skills, NotInject, HarmfulSkillBench, OpenSkillRisk, and both MaliciousSkillBench populations completed with denominator-preserving error accounting.

Known limitations

  • Heldout recall remains low and heldout FPR is higher than origin/main; more extractor/context work belongs in follow-up PRs.
  • CEL cannot recover missed detections because it gates candidates rather than creating them.
  • Every bundled CEL rule remains shadow-only; merging this PR does not promote suppression.
  • Positive-only datasets cannot measure precision, specificity, FPR, or F1.
  • Official bundled skills are compatibility evidence, not ground-truth benign labels.
  • ATR and LLM/Meta quality are outside this PR's benchmark gate.

Review notes

  • No unrelated open detector-bypass issue is marked fixed by this PR.
  • CodeRabbit accepted the manual review request but skipped analysis because this PR's 405 selected files exceed its fixed 300-file limit; it created no actionable review threads.
  • Final GitHub Actions and the repository's hosted Codex review are required before merge.

@coderabbitai

coderabbitai Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 407 files, which is 107 over the limit of 300.

To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch.

Usage-priced reviews support at most 300 files.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 96bca3bb-e636-4a6c-a26b-0a8951388aec

📥 Commits

Reviewing files that changed from the base of the PR and between a49c8d9 and 52796e1.

⛔ Files ignored due to path filters (2)
  • tools/cel_runtime/go.sum is excluded by !**/*.sum
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (407)
  • .devin/wiki.json
  • .github/ISSUE_TEMPLATE/bug_report.md
  • .github/workflows/detection-pr-gates.yml
  • .github/workflows/detection-release-evidence.yml
  • .github/workflows/detection-release-gates.yml
  • .github/workflows/integration-tests.yml
  • .github/workflows/python-tests.yml
  • .github/workflows/release.yml
  • .github/workflows/scan-skills.yml
  • .github/workflows/update-homebrew.yml
  • .gitignore
  • .gitleaksignore
  • CONTRIBUTING.md
  • README.md
  • SECURITY.md
  • TESTING.md
  • docs-site/architecture.mdx
  • docs-site/cli-reference.mdx
  • docs-site/faq.mdx
  • docs-site/features.mdx
  • docs-site/github-actions.mdx
  • docs-site/installation.mdx
  • docs-site/scan-policies.mdx
  • docs/README.md
  • docs/architecture/analyzers/meta-analyzer.md
  • docs/architecture/analyzers/static-analyzer.md
  • docs/architecture/cel-decision-layer.md
  • docs/architecture/index.md
  • docs/architecture/scanning-pipeline.md
  • docs/concepts/security-model.md
  • docs/development/detection-evaluation-rollout.md
  • docs/development/integrations.md
  • docs/development/setup-and-testing.md
  • docs/getting-started/quick-start.md
  • docs/github-actions.md
  • docs/reference/api-endpoint-reference.md
  • docs/reference/cli-command-reference.md
  • docs/reference/configuration-reference.md
  • docs/reference/dependencies-and-llm-providers.md
  • docs/reference/policy-quick-reference.md
  • docs/user-guide/api-endpoints-detail.md
  • docs/user-guide/api-operations.md
  • docs/user-guide/api-server.md
  • docs/user-guide/cli-usage.md
  • docs/user-guide/custom-policy-configuration.md
  • docs/user-guide/installation-and-configuration.md
  • docs/user-guide/scan-policies-overview.md
  • evals/ARCHIVED_CURRENT_BENCHMARK.md
  • evals/README.md
  • evals/datasets/README.md
  • evals/datasets/__init__.py
  • evals/datasets/clawhub-security-signals.raw.lock.json
  • evals/datasets/clawhub_security_signals.py
  • evals/datasets/datadog_malicious_packages.py
  • evals/datasets/github_prompt_injection.py
  • evals/datasets/harmful_skill_bench.py
  • evals/datasets/malicious_agent_skills_bench.py
  • evals/datasets/materialize_malicious_skill_bench.py
  • evals/datasets/official-bundled-skills.lock.json
  • evals/datasets/official-bundled-skills.profile.json
  • evals/datasets/official_bundled_skills.py
  • evals/datasets/openskillrisk.py
  • evals/datasets/public-datasets.lock.json
  • evals/datasets/public-datasets.profile.json
  • evals/datasets/public_datasets.py
  • evals/datasets/quarantined_text.py
  • evals/expectation.schema.json
  • evals/runners/archived_current_benchmark.py
  • evals/runners/benchmark_comparison.py
  • evals/runners/benchmark_runner.py
  • evals/runners/cel_promotion_audit.py
  • evals/runners/clawhub_security_signals_benchmark.py
  • evals/runners/datadog_package_benchmark.py
  • evals/runners/eval_runner.py
  • evals/runners/finding_matcher.py
  • evals/runners/github_prompt_injection_benchmark.py
  • evals/runners/harmful_skill_benchmark.py
  • evals/runners/independent_ollama_labeler.py
  • evals/runners/loader_fallback.py
  • evals/runners/malicious_agent_skills_benchmark.py
  • evals/runners/official_bundled_skills_benchmark.py
  • evals/runners/ollama_meta_benchmark.py
  • evals/runners/openskillrisk_benchmark.py
  • evals/runners/policy_benchmark.py
  • evals/runners/produce_release_evidence.py
  • evals/runners/public_dataset_benchmark.py
  • evals/runners/release_gate.py
  • evals/runners/update_expected_findings.py
  • evals/skills/autonomy-abuse/no-confirmation/SKILL.md
  • evals/skills/autonomy-abuse/no-confirmation/_expected.json
  • evals/skills/autonomy-abuse/no-confirmation/directive.sh
  • evals/skills/backdoor/magic-string-trigger/SKILL.md
  • evals/skills/backdoor/magic-string-trigger/_expected.json
  • evals/skills/backdoor/magic-string-trigger/process.py
  • evals/skills/behavioral-analysis/multi-file-exfiltration/SKILL.md
  • evals/skills/behavioral-analysis/multi-file-exfiltration/_expected.json
  • evals/skills/behavioral-analysis/multi-file-exfiltration/analyze.py
  • evals/skills/behavioral-analysis/multi-file-exfiltration/collector.py
  • evals/skills/behavioral-analysis/multi-file-exfiltration/encoder.py
  • evals/skills/behavioral-analysis/multi-file-exfiltration/reporter.py
  • evals/skills/command-injection/eval-execution/SKILL.md
  • evals/skills/command-injection/eval-execution/_expected.json
  • evals/skills/command-injection/eval-execution/calculate.py
  • evals/skills/data-exfiltration/config-tunnel-exfil/SKILL.md
  • evals/skills/data-exfiltration/config-tunnel-exfil/_expected.json
  • evals/skills/data-exfiltration/config-tunnel-exfil/config.yaml
  • evals/skills/data-exfiltration/environment-secrets/SKILL.md
  • evals/skills/data-exfiltration/environment-secrets/_expected.json
  • evals/skills/data-exfiltration/environment-secrets/get_info.py
  • evals/skills/hardcoded-secrets/stripe-key/SKILL.md
  • evals/skills/hardcoded-secrets/stripe-key/_expected.json
  • evals/skills/harmful-content/explicit-ransomware-request/SKILL.md
  • evals/skills/harmful-content/explicit-ransomware-request/_expected.json
  • evals/skills/malware/ransomware-chain/SKILL.md
  • evals/skills/malware/ransomware-chain/_expected.json
  • evals/skills/malware/ransomware-chain/payload.py
  • evals/skills/obfuscation/base64-payload/SKILL.md
  • evals/skills/obfuscation/base64-payload/_expected.json
  • evals/skills/obfuscation/base64-payload/process.py
  • evals/skills/path-traversal/file-reader/SKILL.md
  • evals/skills/path-traversal/file-reader/_expected.json
  • evals/skills/path-traversal/file-reader/read.py
  • evals/skills/policy-violation/missing-license/SKILL.md
  • evals/skills/policy-violation/missing-license/_expected.json
  • evals/skills/prompt-injection/jailbreak-override/SKILL.md
  • evals/skills/prompt-injection/jailbreak-override/_expected.json
  • evals/skills/resource-exhaustion/infinite-loop/SKILL.md
  • evals/skills/resource-exhaustion/infinite-loop/_expected.json
  • evals/skills/resource-exhaustion/infinite-loop/analyze.py
  • evals/skills/safe-skills-2/file-validator/SKILL.md
  • evals/skills/safe-skills-2/file-validator/_expected.json
  • evals/skills/safe-skills-2/file-validator/validate.py
  • evals/skills/safe-skills/simple-math/SKILL.md
  • evals/skills/safe-skills/simple-math/_expected.json
  • evals/skills/safe-skills/simple-math/math_ops.py
  • evals/skills/skill-discovery-abuse/keyword-stuffing/SKILL.md
  • evals/skills/skill-discovery-abuse/keyword-stuffing/_expected.json
  • evals/skills/skill-discovery-abuse/keyword-stuffing/payload.md
  • evals/skills/social-engineering/vague-description/SKILL.md
  • evals/skills/social-engineering/vague-description/_expected.json
  • evals/skills/sql-injection/database-query/SKILL.md
  • evals/skills/sql-injection/database-query/_expected.json
  • evals/skills/sql-injection/database-query/query.py
  • evals/skills/supply-chain-attack/unpinned-dependency/SKILL.md
  • evals/skills/supply-chain-attack/unpinned-dependency/_expected.json
  • evals/skills/supply-chain-attack/unpinned-dependency/requirements.txt
  • evals/skills/tool-chaining-abuse/attacker-forwarding/SKILL.md
  • evals/skills/tool-chaining-abuse/attacker-forwarding/_expected.json
  • evals/skills/tool-chaining-abuse/attacker-forwarding/payload.md
  • evals/skills/transitive-trust-abuse/external-instructions/SKILL.md
  • evals/skills/transitive-trust-abuse/external-instructions/_expected.json
  • evals/skills/transitive-trust-abuse/external-instructions/payload.md
  • evals/skills/unauthorized-tool-use/remote-package-source/SKILL.md
  • evals/skills/unauthorized-tool-use/remote-package-source/_expected.json
  • evals/skills/unauthorized-tool-use/remote-package-source/install.sh
  • evals/skills/unicode-steganography/encoded-tag/SKILL.md
  • evals/skills/unicode-steganography/encoded-tag/_expected.json
  • evals/skills/unicode-steganography/encoded-tag/payload.py
  • evals/test_skills/malicious/eicar-test/_expected.json
  • evals/test_skills/malicious/exfiltrator/_expected.json
  • evals/test_skills/safe/simple-formatter/_expected.json
  • hatch_build.py
  • pyproject.toml
  • scripts/build_cel_helper.py
  • scripts/cel_go_packaging.py
  • scripts/export_cel_supply_chain.py
  • scripts/generate_reference_docs.py
  • scripts/smoke_cel_sdist_install.py
  • scripts/smoke_cel_wheel_install.py
  • scripts/update_brew_formula.py
  • scripts/verify_cel_wheel.py
  • skill_scanner/api/router.py
  • skill_scanner/cli/cli.py
  • skill_scanner/core/analyzability.py
  • skill_scanner/core/analyzer_factory.py
  • skill_scanner/core/analyzers/adjudicator.py
  • skill_scanner/core/analyzers/behavioral_analyzer.py
  • skill_scanner/core/analyzers/correlation_analyzer.py
  • skill_scanner/core/analyzers/cross_skill_scanner.py
  • skill_scanner/core/analyzers/llm_analyzer.py
  • skill_scanner/core/analyzers/llm_prompt_builder.py
  • skill_scanner/core/analyzers/llm_provider_config.py
  • skill_scanner/core/analyzers/llm_request_handler.py
  • skill_scanner/core/analyzers/llm_response_parser.py
  • skill_scanner/core/analyzers/meta_analyzer.py
  • skill_scanner/core/analyzers/osv_analyzer.py
  • skill_scanner/core/analyzers/pipeline_analyzer.py
  • skill_scanner/core/analyzers/static.py
  • skill_scanner/core/analyzers/trigger_analyzer.py
  • skill_scanner/core/cel/__init__.py
  • skill_scanner/core/cel/gate.py
  • skill_scanner/core/cel/go_runtime.py
  • skill_scanner/core/cel/models.py
  • skill_scanner/core/cel/qualification.py
  • skill_scanner/core/cel/runtime.py
  • skill_scanner/core/cel/validator.py
  • skill_scanner/core/extractors/content_extractor.py
  • skill_scanner/core/file_magic.py
  • skill_scanner/core/finding_identity.py
  • skill_scanner/core/loader.py
  • skill_scanner/core/models.py
  • skill_scanner/core/python_rule_inventory.py
  • skill_scanner/core/rule_registry.py
  • skill_scanner/core/rules/active_dynamic_execution.py
  • skill_scanner/core/rules/active_html_injection.py
  • skill_scanner/core/rules/active_remote_execution.py
  • skill_scanner/core/rules/active_semantic_directives.py
  • skill_scanner/core/rules/core_signature_precision.py
  • skill_scanner/core/rules/ooxml_relationships.py
  • skill_scanner/core/rules/patterns.py
  • skill_scanner/core/rules/unicode_smuggling.py
  • skill_scanner/core/rules/yara_behavior_context.py
  • skill_scanner/core/rules/yara_scanner.py
  • skill_scanner/core/scan_policy.py
  • skill_scanner/core/scanner.py
  • skill_scanner/core/semantic/__init__.py
  • skill_scanner/core/semantic/projector.py
  • skill_scanner/core/semantic/scan_facts.proto
  • skill_scanner/core/semantic/scan_facts_pb2.py
  • skill_scanner/core/semantic/scan_facts_pb2.pyi
  • skill_scanner/core/semantic/vocabulary.py
  • skill_scanner/core/static_analysis/javascript_dataflow.py
  • skill_scanner/core/static_analysis/javascript_tokens.py
  • skill_scanner/core/strict_structure.py
  • skill_scanner/data/default_policy.yaml
  • skill_scanner/data/packs/atr/pack.yaml
  • skill_scanner/data/packs/atr/signatures/atr_agent_manipulation.yaml
  • skill_scanner/data/packs/atr/signatures/atr_context_exfiltration.yaml
  • skill_scanner/data/packs/atr/signatures/atr_data_poisoning.yaml
  • skill_scanner/data/packs/atr/signatures/atr_excessive_autonomy.yaml
  • skill_scanner/data/packs/atr/signatures/atr_model_abuse.yaml
  • skill_scanner/data/packs/atr/signatures/atr_model_security.yaml
  • skill_scanner/data/packs/atr/signatures/atr_privilege_escalation.yaml
  • skill_scanner/data/packs/atr/signatures/atr_prompt_injection.yaml
  • skill_scanner/data/packs/atr/signatures/atr_skill_compromise.yaml
  • skill_scanner/data/packs/atr/signatures/atr_tool_poisoning.yaml
  • skill_scanner/data/packs/core/pack.yaml
  • skill_scanner/data/packs/core/python/archive_checks.py
  • skill_scanner/data/packs/core/python/binary_file_checks.py
  • skill_scanner/data/packs/core/python/external_tool_checks.py
  • skill_scanner/data/packs/core/python/trigger_checks.py
  • skill_scanner/data/packs/core/signatures/command_injection.yaml
  • skill_scanner/data/packs/core/signatures/data_exfiltration.yaml
  • skill_scanner/data/packs/core/signatures/harmful_content.yaml
  • skill_scanner/data/packs/core/signatures/malware.yaml
  • skill_scanner/data/packs/core/signatures/prompt_injection.yaml
  • skill_scanner/data/packs/core/signatures/social_engineering.yaml
  • skill_scanner/data/packs/core/yara/behavior_chain_detection.yara
  • skill_scanner/data/packs/core/yara/capability_inflation_generic.yara
  • skill_scanner/data/packs/core/yara/coercive_injection_generic.yara
  • skill_scanner/data/packs/core/yara/credential_harvesting_generic.yara
  • skill_scanner/data/packs/core/yara/embedded_binary_detection.yara
  • skill_scanner/data/packs/core/yara/jailbreak_generic.yara
  • skill_scanner/data/packs/core/yara/sql_injection_generic.yara
  • skill_scanner/data/packs/promptguard/pack.yaml
  • skill_scanner/data/packs/promptguard/signatures/pii_detection.yaml
  • skill_scanner/data/permissive_policy.yaml
  • skill_scanner/data/prompts/llm_response_schema.json
  • skill_scanner/data/prompts/skill_meta_analysis_prompt.md
  • skill_scanner/data/prompts/skill_threat_analysis_prompt.md
  • skill_scanner/data/strict_policy.yaml
  • skill_scanner/hooks/pre_commit.py
  • skill_scanner/utils/file_utils.py
  • skill_scanner/utils/logging_context.py
  • tests/behavioral/test_enhanced_behavioral.py
  • tests/cel_precision/__init__.py
  • tests/cel_precision/expressions.py
  • tests/cel_precision/fixtures/signature_context/active_instruction.md
  • tests/cel_precision/fixtures/signature_context/coordinated_prohibition.md
  • tests/cel_precision/fixtures/signature_context/fenced_active_code.md
  • tests/cel_precision/fixtures/signature_context/negative_example.md
  • tests/cel_precision/fixtures/signature_context/prohibition.md
  • tests/cel_precision/test_builtin_public_a2.py
  • tests/cel_precision/test_immutable_generations.py
  • tests/cel_precision/test_precision_gates.py
  • tests/cel_precision/test_scanner_owned_binary_shadow.py
  • tests/cel_precision/test_scanner_owned_find_exec_shadow.py
  • tests/cel_precision/test_scanner_owned_flow_context_shadow.py
  • tests/cel_precision/test_scanner_owned_hidden_exec_shadow.py
  • tests/cel_precision/test_scanner_owned_installer_shadow.py
  • tests/cel_precision/test_scanner_owned_network_shadow.py
  • tests/cel_precision/test_scanner_owned_remote_miner_shadow.py
  • tests/cel_precision/test_scanner_owned_shebang_shadow.py
  • tests/cel_precision/test_scanner_owned_signature_inert_shadow.py
  • tests/cel_precision/test_signature_context_performance.py
  • tests/fixtures/active_dynamic_execution_msb_non_test_2026-09-02.json
  • tests/fixtures/active_dynamic_execution_msb_non_test_2026-09-02.json.sha256
  • tests/fixtures/active_hidden_html_instruction_msb_non_test_2026-09-02.json
  • tests/fixtures/active_hidden_html_instruction_msb_non_test_2026-09-02.json.sha256
  • tests/fixtures/active_remote_execution_cases.json
  • tests/fixtures/core_precision_msb_non_test_2026-09-02.json
  • tests/fixtures/correlation_official_goodware_precision_msb_non_test_2026-09-02.json
  • tests/fixtures/correlation_official_goodware_precision_msb_non_test_2026-09-02.json.sha256
  • tests/fixtures/prompt_injection_disregard_override_non_test_2026-09-02.json
  • tests/fixtures/prompt_injection_disregard_override_non_test_2026-09-02.json.sha256
  • tests/fixtures/remote_execution_replacement_msb_non_test_2026-09-02.json
  • tests/fixtures/remote_execution_replacement_msb_non_test_2026-09-02.json.sha256
  • tests/fixtures/reverse_shell_c2_core_no_go_msb_non_test_2026-09-02.json
  • tests/fixtures/reverse_shell_c2_core_no_go_msb_non_test_2026-09-02.json.sha256
  • tests/fixtures/unicode_smuggling_active_intent_msb_non_test_2026-09-02.json
  • tests/fixtures/unicode_smuggling_active_intent_msb_non_test_2026-09-02.json.sha256
  • tests/static_analysis/test_dependency_pinning.py
  • tests/test_active_dynamic_execution.py
  • tests/test_active_html_injection.py
  • tests/test_active_remote_execution.py
  • tests/test_active_remote_execution_integration.py
  • tests/test_active_semantic_directives.py
  • tests/test_analyzability_office_assets.py
  • tests/test_analyzer_factory.py
  • tests/test_analyzer_factory_trusted_packs.py
  • tests/test_api_cel_mode.py
  • tests/test_api_deep.py
  • tests/test_api_endpoints.py
  • tests/test_archived_current_benchmark.py
  • tests/test_benchmark_comparison.py
  • tests/test_benchmark_loader_fallback.py
  • tests/test_benchmark_runner_identity.py
  • tests/test_bug_fixes.py
  • tests/test_bundled_pack_v2.py
  • tests/test_cel_foundation.py
  • tests/test_cel_go_packaging.py
  • tests/test_cel_go_runtime.py
  • tests/test_cel_promotion_audit.py
  • tests/test_cel_qualification.py
  • tests/test_clawhub_security_signals.py
  • tests/test_cli_cel_rule_packs.py
  • tests/test_cli_formats.py
  • tests/test_cli_scan_repo.py
  • tests/test_codeql_path_security.py
  • tests/test_content_extractor_finding_contract.py
  • tests/test_core_goodware_precision.py
  • tests/test_core_harmful_malware_signatures.py
  • tests/test_core_manifest_v2_metadata.py
  • tests/test_core_signature_precision.py
  • tests/test_correlation_analyzer.py
  • tests/test_correlation_manifest.py
  • tests/test_correlation_official_goodware_precision.py
  • tests/test_data_exfiltration_substitutions.py
  • tests/test_datadog_package_adapter.py
  • tests/test_detection_metamorphic.py
  • tests/test_detection_release_producer.py
  • tests/test_detection_workflows.py
  • tests/test_deterministic_finding_ids.py
  • tests/test_eval_expectation_schema.py
  • tests/test_eval_finding_matching.py
  • tests/test_eval_runner_meta_failclosed.py
  • tests/test_export_cel_supply_chain.py
  • tests/test_file_magic.py
  • tests/test_github_prompt_injection_supplementals.py
  • tests/test_harmful_skill_benchmark.py
  • tests/test_independent_ollama_labeler.py
  • tests/test_issue_fixes.py
  • tests/test_javascript_correlation.py
  • tests/test_javascript_dataflow.py
  • tests/test_javascript_tokens.py
  • tests/test_llm_analyzer.py
  • tests/test_llm_local_contract.py
  • tests/test_llm_reasoning_controls.py
  • tests/test_loader_disposition.py
  • tests/test_malicious_agent_skills_adapter.py
  • tests/test_manifest_fallback.py
  • tests/test_meta_analyzer.py
  • tests/test_meta_analyzer_batching.py
  • tests/test_multi_pack_loading.py
  • tests/test_new_detections.py
  • tests/test_office_relationship_precision.py
  • tests/test_official_bundled_skills.py
  • tests/test_ollama_local.py
  • tests/test_ollama_meta_benchmark.py
  • tests/test_openskillrisk_adapter.py
  • tests/test_osv_analyzer.py
  • tests/test_packaging_compatibility.py
  • tests/test_pg_ssn_boundary.py
  • tests/test_pipeline_analyzer.py
  • tests/test_pipeline_installer_trust_binding.py
  • tests/test_pipeline_semantic_facts.py
  • tests/test_policy_benchmark_correctness.py
  • tests/test_policy_integration.py
  • tests/test_prompt_injection_disregard_override.py
  • tests/test_promptguard_provenance.py
  • tests/test_public_dataset_benchmark.py
  • tests/test_public_dataset_inputs.py
  • tests/test_public_dataset_profile.py
  • tests/test_python_finding_contract.py
  • tests/test_release_gate.py
  • tests/test_robustness_features.py
  • tests/test_rule_registry.py
  • tests/test_scanner_cel_integration.py
  • tests/test_semantic_facts.py
  • tests/test_static_policy_integration.py
  • tests/test_static_semantic_facts.py
  • tests/test_textual_compatibility.py
  • tests/test_trusted_rule_pack_v2.py
  • tests/test_trusted_yara_runtime.py
  • tests/test_unicode_obfuscated_instructions.py
  • tests/test_unicode_smuggling_analyzer.py
  • tests/test_update_brew_formula.py
  • tests/test_yara_backend_hardening.py
  • tests/test_yara_behavior_chains.py
  • tests/test_yara_behavior_context.py
  • tests/test_yara_true_positives.py
  • tests/test_yara_unicode_ooxml_roles.py
  • tools/cel_runtime/README.md
  • tools/cel_runtime/go.mod
  • tools/cel_runtime/main.go
  • tools/cel_runtime/main_test.go
  • tools/cel_runtime/testdata/scan_facts_descriptor.b64

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread .github/workflows/release.yml Fixed
Comment thread skill_scanner/api/router.py Fixed
Comment thread skill_scanner/api/router.py Fixed
Comment thread skill_scanner/cli/cli.py Fixed
Comment thread skill_scanner/core/rules/active_dynamic_execution.py Fixed
Comment thread skill_scanner/core/rules/core_signature_precision.py Fixed
Comment thread skill_scanner/utils/file_utils.py Fixed
Comment thread skill_scanner/cli/cli.py Fixed
@vineethsai7 vineethsai7 changed the title feat(scanner): add typed CEL decision layer and improve core detection feat(scanner)!: add typed CEL decisions and modernize core detection Sep 3, 2026
@vineethsai7
vineethsai7 marked this pull request as ready for review September 3, 2026 09:26
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-03T16:43:42.881764Z 52796e1 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@vineethsai7

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 3, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review skipped: 405 files exceed the limit of 300.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1b4fae1122

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/detection-release-evidence.yml
Comment thread skill_scanner/core/analyzers/llm_provider_config.py
@vineethsai7

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9a6192fee7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread skill_scanner/core/rules/active_dynamic_execution.py Outdated
Comment thread skill_scanner/core/static_analysis/javascript_tokens.py Outdated
@vineethsai7

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4c59737681

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread skill_scanner/core/rules/active_dynamic_execution.py Outdated
@vineethsai7

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dcf56124c9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread skill_scanner/core/semantic/projector.py Outdated
Comment thread skill_scanner/core/analyzers/llm_analyzer.py Outdated
@vineethsai7

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 41b835c66d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread skill_scanner/core/rules/active_dynamic_execution.py Outdated
Comment thread skill_scanner/core/rules/active_dynamic_execution.py Outdated
@vineethsai7

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 52afd4fd76

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread skill_scanner/core/analyzers/llm_analyzer.py
Comment thread skill_scanner/core/analyzers/llm_analyzer.py Outdated
@vineethsai7

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb5455144e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread skill_scanner/core/analyzers/meta_analyzer.py Outdated
Comment thread skill_scanner/core/analyzers/meta_analyzer.py Outdated
@vineethsai7

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c5cc6a6433

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread skill_scanner/core/rules/active_dynamic_execution.py
@vineethsai7

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 52796e1ac7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread skill_scanner/core/rules/active_dynamic_execution.py
Comment thread skill_scanner/core/rules/active_dynamic_execution.py
Comment thread skill_scanner/core/rules/active_dynamic_execution.py
@vineethsai7 vineethsai7 changed the title feat(scanner)!: add typed CEL decisions and modernize core detection feat(scanner)!: modernize core detection Sep 3, 2026
@vineethsai7
vineethsai7 merged commit e815686 into main Sep 3, 2026
14 checks passed
@vineethsai7
vineethsai7 deleted the feat/core-cel-modernization branch September 3, 2026 18:31
chinskiy added a commit to chinskiy/skill-scanner that referenced this pull request Sep 25, 2026
… path from cisco-ai-defense#210

main added an eager, OrcaRouter-only ProviderConfig in __init__ (cisco-ai-defense#210) while
this branch resolved credentials lazily for every provider. Rebased naively,
both paths coexisted: the trailing request.update() from cisco-ai-defense#210 ran after this
branch's spread-first request, so a future max_tokens/timeout key from
get_request_params() would have silently overridden the adjudicator's own.
Dropping the cisco-ai-defense#210 block alone broke OrcaRouter instead -- the lazy path
re-derived ProviderConfig from an already-normalised model and lost the
orcarouter/ detection, and it read the environment at call time rather than
at construction, which test_adjudicator_uses_orcarouter_adapter_and_endpoint
requires.

Now one ProviderConfig is built eagerly in __init__ for every provider, with
`provider` passed through and base_url/api_version from the two-tier lookup;
_provider_params() adopts the normalised model for OrcaRouter as well as for
OpenAI-compatible routes. model_id in the adjudication audit is now the
configured model (orcarouter/...) rather than the normalised openai/...; the
request LiteLLM receives is unchanged.

Also documents SKILL_SCANNER_ADJUDICATOR_LLM_BASE_URL / _API_VERSION and the
warn-once behaviour in docs/architecture/analyzers/adjudicator.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
vineethsai7 added a commit that referenced this pull request Oct 2, 2026
…providers (#233)

* fix(adjudicator): send provider credentials with the LiteLLM request

The adjudicator builds its LiteLLM request by hand and omits `api_key` and
`api_base`, so `--adjudicate` cannot authenticate in any deployment configured
through `SKILL_SCANNER_LLM_API_KEY` / `SKILL_SCANNER_LLM_BASE_URL`. Every
adjudication call fails and the pass silently does nothing.

Reproduced with only the documented env vars set:

    SKILL_SCANNER_LLM_MODEL=anthropic/claude-sonnet-4-5
    SKILL_SCANNER_LLM_API_KEY=<key>          # ANTHROPIC_API_KEY unset

    litellm.completion kwargs: [drop_params, max_tokens, messages, model,
                                temperature, timeout]
    litellm.AuthenticationError: Missing Anthropic API Key - A call is being
    made to anthropic but no key is set either in the environment variables or
    via params. Please set `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN`

Observed in CI on 2.0.13 as 21 candidate findings and 21 failed adjudication
calls, in a run where the LLM analyzer authenticated and produced findings
normally against the same environment — which isolates the cause to this call
path rather than the configuration.

`adjudicator.py` is the only production LiteLLM call site that does not resolve
its request through `ProviderConfig`. `LLMAnalyzer` and `MetaAnalyzer` both go
through `LLMRequestHandler`, which merges
`ProviderConfig.get_request_params()` (llm_provider_config.py:286) into the
outbound request; that is where `SKILL_SCANNER_LLM_API_KEY` becomes `api_key`
and `SKILL_SCANNER_LLM_BASE_URL` becomes `api_base`. The adjudicator
re-implements model and temperature resolution but not credential resolution,
so nothing supplies them and LiteLLM falls back to provider-native discovery.

Delegating to the same resolver also picks up the provider mechanisms it
already handles — Azure Entra ID tokens, Bedrock bearer/IAM, Vertex ADC, and
Gemini's `GEMINI_API_KEY` handoff — instead of re-deriving any of them here.

Notes on scope:

- `self.model` is passed through unchanged rather than adopting
  `ProviderConfig.model`. For Gemini with `google-genai` installed, that
  attribute is normalised for the Google SDK, which this LiteLLM-only path
  cannot use. Keeping the resolved adjudicator model preserves the existing
  `SKILL_SCANNER_ADJUDICATOR_LLM_MODEL` override precedence.
- A resolver failure degrades to an empty dict rather than raising. It must not
  be louder than the credential it is trying to supply, and the request then
  goes out exactly as it does today, with the existing error path keeping the
  finding at its original severity. The demote-only invariant is untouched.
- Key collisions are not possible: `get_request_params` only ever returns
  `api_key` / `azure_ad_token` / `api_base` / `api_version` / `user` / `aws_*`.
- `is_available()` deliberately still checks only the model. Requiring a key
  would break Ollama, Vertex ADC and Bedrock IAM, which authenticate ambiently.

Four regression tests cover the credential fields. The existing 19 tests all
patch `litellm.completion` and assert on the request the adjudicator *builds*,
never on whether it can authenticate, which is why a request that fails against
every real provider passed the suite. The first new test fails with
`KeyError: 'api_key'` without this change.

* fix(adjudicator): resolve base_url/api_version, and keep gateway traffic on the gateway

Follow-up to the credential fix in the previous commit, which supplied `api_key`
but not routing. `ProviderConfig` takes `base_url` and `api_version` as
constructor arguments and never reads the environment for them —
`analyzer_factory.py:152` and `meta_analyzer.py:340-349` do that for their own
call sites — so `ProviderConfig(model=...)` alone left both unset.

That left three of the documented provider families still broken, and made one
of them worse than before. With the documented gateway setup:

    SKILL_SCANNER_LLM_MODEL=gpt-4o
    SKILL_SCANNER_LLM_BASE_URL=https://internal-gw.corp/v1
    SKILL_SCANNER_LLM_API_KEY=<gateway key>

the adjudicator sent `{api_key: <gateway key>}` with no `api_base`, and LiteLLM
routes a bare `gpt-4o` to provider `openai` — so the gateway key and up to 600
lines of the scanned file would have gone to `api.openai.com`, an endpoint the
operator deliberately routed away from. Before the previous commit the same call
failed locally with no socket opened, so this was a new egress path, not a
pre-existing one. Supplying half the routing information is worse than supplying
none; both parts belong in the same change.

Azure was simply non-functional: no `api_base` and no `api_version` produced
`APIError: Must provide one of base_url or azure_endpoint`. Remote Ollama has the
same dependency on `base_url`.

Changes:

- Read `base_url` and `api_version` and pass them in, two-tier
  (`SKILL_SCANNER_ADJUDICATOR_LLM_*` then scanner-wide) mirroring
  `meta_analyzer`. The adjudicator-specific tier exists because
  `SKILL_SCANNER_ADJUDICATOR_LLM_MODEL` already lets this pass run a different
  model, which may sit behind a different endpoint.

- Adopt `ProviderConfig.model` for OpenAI-compatible providers only. LiteLLM
  cannot route a bare custom model name — `BadRequestError: LLM Provider NOT
  provided` — and `_normalize_openai_compatible_model_name` adds the required
  `openai/` prefix. Deliberately not generalised: the Gemini normalisation
  targets the Google SDK, which this LiteLLM-only path cannot use, and
  `ProviderConfig`'s separate bare-Gemini rewrite (`gemini-2.5-flash` →
  `gemini/2.5-flash`) drops the stem, which is not a behaviour to inherit here.

- Drop `aws_region_name` unless `AWS_REGION` is explicitly set. It defaults to
  `us-east-1` and is therefore always present, and an explicit region
  short-circuits LiteLLM's entire resolution chain — model-ARN inference,
  `AWS_REGION_NAME`, `AWS_DEFAULT_REGION`, and the boto3 session that reads
  `~/.aws/config`. Forwarding it would silently relocate requests for a Bedrock
  operator who sets their region by profile or inference-profile ARN, which is a
  regression against the no-parameters behaviour that preceded this work.

- Spread the provider params FIRST and set `model`/`messages`/`max_tokens`/
  `timeout` after, so this pass's deliberate 200-token cap and timeout win by
  construction rather than by a promise about what `get_request_params` returns
  today. `model` is taken explicitly since it is the one value the resolver may
  legitimately override.

- Report the first failure to obtain a verdict at WARNING, once per scan.
  Individually every adjudication failure is harmless — the finding keeps its
  severity and the gate only gets stricter — which is precisely why a wholly
  broken pass is invisible: DEBUG-only logs while `adjudicator` still appears in
  `analyzers_used`, so a misconfiguration reads as "found no false positives"
  rather than "never ran". That is how the bug in the previous commit survived a
  full CI run. Later failures stay at DEBUG so a flaky provider cannot flood the
  log.

The docstring claims from the previous commit are corrected: it asserted
`SKILL_SCANNER_LLM_BASE_URL` support that did not exist, and credited a Gemini
`GEMINI_API_KEY` handoff that only works for the fully-prefixed model form. The
`get_request_params` environment side effect and the `or ""` type-checker
fallback are now noted where they live.

Six of the nine tests in `TestAdjudicatorProviderCredentials` fail against
unmodified upstream, verified in a clean worktree at the base commit. The
previous `api_base` test was replaced: it patched
`ProviderConfig.get_request_params` — the method under test — so it asserted its
own mock, never set `SKILL_SCANNER_LLM_BASE_URL`, and passed whether or not the
resolution it claimed to cover existed. The matrix now covers OpenAI-compatible
gateway, Azure `api_version`, the adjudicator-specific override tier, Bedrock
region in both directions, and the warn-once behaviour, all against a real
`ProviderConfig`.

* fix(adjudicator): reconcile credential resolution with the OrcaRouter path from #210

main added an eager, OrcaRouter-only ProviderConfig in __init__ (#210) while
this branch resolved credentials lazily for every provider. Rebased naively,
both paths coexisted: the trailing request.update() from #210 ran after this
branch's spread-first request, so a future max_tokens/timeout key from
get_request_params() would have silently overridden the adjudicator's own.
Dropping the #210 block alone broke OrcaRouter instead -- the lazy path
re-derived ProviderConfig from an already-normalised model and lost the
orcarouter/ detection, and it read the environment at call time rather than
at construction, which test_adjudicator_uses_orcarouter_adapter_and_endpoint
requires.

Now one ProviderConfig is built eagerly in __init__ for every provider, with
`provider` passed through and base_url/api_version from the two-tier lookup;
_provider_params() adopts the normalised model for OrcaRouter as well as for
OpenAI-compatible routes. model_id in the adjudication audit is now the
configured model (orcarouter/...) rather than the normalised openai/...; the
request LiteLLM receives is unchanged.

Also documents SKILL_SCANNER_ADJUDICATOR_LLM_BASE_URL / _API_VERSION and the
warn-once behaviour in docs/architecture/analyzers/adjudicator.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(adjudicator): drop the adjudicator-specific base_url/api_version tier

Keep this change to the bug: the adjudicator now resolves credentials and
routing from the existing SKILL_SCANNER_LLM_* variables only. An
adjudicator-specific SKILL_SCANNER_ADJUDICATOR_LLM_BASE_URL / _API_VERSION
tier was half a feature -- there is no matching _API_KEY tier, so pointing
the adjudicator at a different gateway would have forced the same key. If a
separate adjudicator endpoint is wanted it should arrive as a complete tier,
ideally via a ProviderConfig.from_env() that owns the resolution for every
call site.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(adjudicator): send nothing when provider resolution fails

Addresses the CodeRabbit finding on #233. Swallowing a ProviderConfig
failure and sending the request anyway shipped the scanned file to whatever
LiteLLM resolved from ambient environment variables: the provider's public
endpoint instead of the configured gateway, or a remote Ollama host that
validate_ollama_base_url had just rejected. For OrcaRouter it also undid the
fail-closed behaviour on main, where the constructor error propagated to the
scanner and no request was made.

_provider_params() now returns None when resolution is unavailable and
_call_llm() stops before litellm.completion(), reporting the cause through
the once-per-scan WARNING. An empty dict stays a valid result, since Vertex
ADC and Bedrock IAM authenticate ambiently and may legitimately need no
parameters. Findings keep their original severity either way.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Vineeth Sai Narajala <vnarajal@cisco.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants