Skip to content

chore(scanner): upgrade, fix llm_analyzer in Modal, and improve backfill - #344

Open
maresb wants to merge 5 commits into
pymc-labs:mainfrom
maresb:scanner-validation-infra
Open

chore(scanner): upgrade, fix llm_analyzer in Modal, and improve backfill#344
maresb wants to merge 5 commits into
pymc-labs:mainfrom
maresb:scanner-validation-infra

Conversation

@maresb

@maresb maresb commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

What this does

Three #329 blockers and one follow-on. Each was required to produce trustworthy distribution numbers for the Cisco scanner validation batch — they're not "cleanup that fell out of #329".

Why each commit was necessary

3e5cf11 — fix(modal): drop openssh-client from crawler image

make deploy-dev was failing intermittently with 404 Not Found [IP: …] libssl3 / openssl after Debian mirror snapshot rotations, blocking any iteration on the server for #329. Root cause: image.apt_install("git") pulled openssh-client as a recommended dep, which dragged in libssl3 / openssl that were briefly missing from the mirror. The crawler only clones over HTTPS with GitHub App tokens (never SSH), so switching to image.run_commands("apt-get install --no-install-recommends git …") is safe and makes deploys reliable.

03a9f49 — chore(deps): upgrade cisco-ai-skill-scanner 2.0.6 → 2.0.9 (uv.lock)

Pulls upstream scanner improvements (including ones that affect verdict classification) so that #329's numbers reflect the current scanner rather than a stale fork. Also re-syncs the lockfile to declared pyproject versions and drops the old google-generativeai stack (the new scanner reaches Gemini via litellm).

792f37d + de95818 — fix(scanner): depend on cisco-ai-skill-scanner[google]

During the first #329 backfill run, Modal logs showed WARNING | Could not load LLM analyzer on every scan — Cisco was running 2 of its 3 analyzers (behavioral + trigger) and silently skipping llm_analyzer. Any statistics gathered in that state would have understated Cisco's capability and led to the wrong replace/don't-replace decision for #328.

Root cause is an upstream logic flaw in cisco-ai-skill-scanner (filed as cisco-ai/cisco-ai-skill-scanner#97): for Gemini models it raises ImportError when google-genai is absent, even if litellm is installed. My local env had a stray google-genai install that masked it; Modal didn't.

Fix: depend on cisco-ai-skill-scanner[google], which pulls google-genai into the Modal image. After deploy all three analyzers load and the LLM analyzer contributes findings to the #329 batch.

Lockfile changes are in the separate commit de95818 per repo convention.

c9bb2e0 — refactor(backfill): use skills.latest_semver join and add --random flag

Two fixes to backfill_scan_reports.py, both blockers:

  1. Query timeout. The original _find_skills_needing_scan used ROW_NUMBER() OVER (PARTITION BY skill_id ORDER BY semver DESC) to pick the latest version per skill. Against the current versions table this hits the 30s statement_timeout before returning a single row, so the script aborted on startup. Rewrote to join on the denormalized skills.latest_semver column — same result, well under a second.
  2. Alphabetical-head bias. The script previously walked the skill list alphabetically. The first 200 rows are heavily dominated by a handful of publishers, so the initial Run fresh scan batch and compute distribution stats #329 numbers over-represented their patterns. Added --random which appends ORDER BY random() at the SQL level, used to produce the corrected distribution on Run fresh scan batch and compute distribution stats #329.

Test plan

Independent of #337, #342, #343.

maresb added 5 commits April 20, 2026 11:47
…rror rotations

The crawler image build kept failing inside Modal with 404s for
libssl3 / openssl on the Debian security mirror — apt_install("git")
pulls openssh-client as a recommended dep, which then drags in the
openssl chain whose security revisions get rotated faster than the
cached apt package list. Cascade rebuilds (e.g. after a uv.lock bump)
made this trip the live mirror instead of the cached layer.

The crawler clones repos over HTTPS using GitHub App installation
tokens (mint_installation_token), never over SSH — verified that the
git@github.com: references in tracker.py / discovery.py are only URL
parsing, not transport. So --no-install-recommends is safe and also
shrinks the crawler image.

Made-with: Cursor
Pulls in upstream improvements ahead of the validation batch run for
issue pymc-labs#329. Also re-syncs the lockfile to the current declared
pyproject versions (dhub-cli 0.12.3, dhub-core 0.2.10) and drops the
google-generativeai stack — the new scanner now reaches Gemini via
litellm only, and no first-party code imports google.* directly.

Made-with: Cursor
…r loads in Modal

The scanner's LLMProviderConfig raises ImportError for Gemini models when
google-genai is missing, even if LiteLLM (which also supports Gemini) is
installed — see cisco-ai-defense/skill-scanner#97. Our local dev env had
a stray google-genai installation that masked the bug; in Modal the
llm_analyzer was silently disabled.

Pulling google-genai via the [google] extra makes llm_analyzer available
on every scan in the deployed image until the upstream is fixed.

Made-with: Cursor
Pulls google-genai and its transitive deps into the workspace lock so
the [google] extra from the previous commit resolves deterministically.

Made-with: Cursor
Two related changes to make the scan-report backfill usable at scale
and able to produce representative samples:

- Replace the ROW_NUMBER() OVER (PARTITION BY skill_id ORDER BY
  created_at DESC) subquery with a direct join on the denormalized
  skills.latest_semver pointer. The window-function variant trips the
  30s statement_timeout on the current versions table; the new query
  runs in well under a second.

- Add a --random flag that swaps the ORDER BY from (org_slug, name)
  to random(). Alphabetical ordering concentrated --limit N batches
  on a handful of orgs (e.g. 77% of the first 200-skill run landed
  on a single prolific publisher); random order spreads the same
  sample across ~80 orgs with no single org over 10%, which is what
  we want for the distribution stats in pymc-labs#329.

Made-with: Cursor
@maresb
maresb force-pushed the scanner-validation-infra branch from d90b7e6 to c9bb2e0 Compare April 20, 2026 09:58
@maresb
maresb marked this pull request as ready for review April 20, 2026 10:01
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.

1 participant