Skip to content

docs(security): annotate CWE-502 pickle/torch.load sites (Refs helicalAI/dashboard#1154) - #384

Closed
oriolpetithelical wants to merge 1 commit into
releasefrom
fix/1154-pickle-cwe502-annotations
Closed

docs(security): annotate CWE-502 pickle/torch.load sites (Refs helicalAI/dashboard#1154)#384
oriolpetithelical wants to merge 1 commit into
releasefrom
fix/1154-pickle-cwe502-annotations

Conversation

@oriolpetithelical

Copy link
Copy Markdown
Contributor

What & why

Annotation-only PR for the CWE-502 (unsafe deserialization) scan findings tracked in helicalAI/dashboard#1154. No executable code is changed — every added line is a comment; all touched files still compile.

A 3-line block is added directly above each of the 16 flagged pickle.load / pd.read_pickle / torch.load / pickle.dump sites in helical/models/, recording:

  1. Finding — the GitHub blob URL of the scan finding.
  2. Reached from dags repo — which Airflow DAG(s) ultimately invoke the call (DAG → container script → helical function), or none with a note on how it is actually run.
  3. Counterpart — where the pickle is written vs. read (in-repo path:line, an external S3/HF artifact, or "no in-repo reader").

Note: the scan also flagged blocks.py:452 (np.load, numpy-in-pytorch-modules). That is a performance lint with no CWE and is not a pickle/deserialization issue, so it is intentionally not annotated here.

Triage summary (from the trace)

  • Live DAG-reachable (prioritise for the fix): geneformer tokenizer (×3), scgpt, transcriptformer, hyena — all load artifacts downloaded from the helicalpackage S3 bucket via finetuning/embedding/attention_analysis/perturbation/evaluation DAGs (+ nebius finetuning variants).
  • Not reached by any DAG: all 4 UCE sites, both Tahoe sites, both base_models sites (UCE/Tahoe run only via examples/notebooks; base_models.load_model is bypassed by bio-agent's own loader).
  • Only pair with an in-repo writer: base_models.py:296 (save_modeltorch.save) — cleanest safe-load fix candidate.
  • Dead code: geneformer_utils.py:38 writes a pickle nothing reads.

Verification

  • git diff confirms only comment lines added, nothing removed.
  • python -m py_compile passes on all 11 files.
  • Every trace (DAG dag_ids, container scripts, S3 keys, read/write counterparts) was independently re-verified by adversarial sub-agents against helical@release and dags@develop.

Refs helicalAI/dashboard#1154

🤖 Generated with Claude Code

…ch and read/write counterpart

Add a 3-line comment block above each of the 16 CWE-502 pickle / torch.load
findings in helical/models. Annotation only -- no executable code changed
(all added lines are comments; files still compile).

Each block records:
  1. the finding URL,
  2. which Airflow DAG(s) in the dags repo reach the call (DAG -> container
     script -> helical function), or "none" with how it is actually run,
  3. the read<->write pickle counterpart (in-repo path:line, external S3/HF
     artifact, or "no in-repo reader").

Traces were cross-checked by independent sub-agents against helical@release
and dags@develop.

Refs #1154

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PjQewSrSBRm13a9M2hm5hE
@oriolpetithelical oriolpetithelical self-assigned this Jul 9, 2026
oriolpetithelical added a commit that referenced this pull request Jul 10, 2026
…rep) (#385)

* fix(security): harden CWE-502 torch.load sites with weights_only + nosemgrep

Follow-up to the annotation-only PR #384. Mitigates the CWE-502 (unsafe
deserialization) scan findings from helicalAI/dashboard#1154 for the
torch.load sites, and suppresses the Trail of Bits scanner on the sites that
are now safe or unavoidably require full unpickling.

Groups A + B (torch.load) addressed here; group C (raw pickle.load of gene
dictionaries) is intentionally deferred to a follow-up.

- weights_only=True (real mitigation) added to the loads whose payloads are
  tensors / plain state dicts: scgpt best_model.pt, uce all_tokens.torch, uce
  gene-embedding dicts, and the uce model-weights load (the last was missed by
  the Bastion scan but hardened for consistency). Under the pinned torch 2.7.0
  this matches the existing default, so it is a no-op at runtime and only
  hardens intent.
- HyenaDNA keeps weights_only=False: its Lightning-style .ckpt carries
  non-tensor objects the safe loader rejects. Documented + suppressed with a
  "trusted sources" justification (a try-safe-first attempt was rejected in
  review: it fired a misleading CWE-502 warning and double-read the file on
  every legitimate load).
- base_models legacy fallback keeps weights_only=False (pre-v2.0.0 full-model
  pickles); documented + suppressed, reached only when the safe load fails.
- Every torch.load carries a `# nosemgrep:
  trailofbits.python.pickles-in-pytorch.pickles-in-pytorch` on the line
  immediately preceding the call (Semgrep only honors adjacent suppressions)
  plus a per-site CWE-502 justification.

Validated: scGPT and HyenaDNA load real downloaded checkpoints end-to-end;
py_compile passes on all touched files. CI exercises these load paths against
real (non-mocked) upstream artifacts.

Refs helicalAI/dashboard#1154
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