feat(analyzer): detect insecure deserialization (AST10, TT6, DS1–DS4) - #246
Conversation
Closes the insecure-deserialization gap (OWASP ASI05 - Unexpected Code Execution) across the analyzer stack: - behavioral_ast (AST10): flags pickle / marshal / dill / jsonpickle / joblib / pandas.read_pickle, plus argument-aware yaml.load, torch.load, and numpy.load so the hardened forms (SafeLoader, weights_only=True, default allow_pickle=False) are not false-positived. - behavioral_taint_tracking (TT6): external or file input -> deserialization sink, the RCE-class flow analogue of TT5. - static_patterns_deserialization (DS1-DS4): language-gated regex breadth for the non-Python scripts a skill may bundle (PHP unserialize, Ruby Marshal/YAML/Oj, JS node-serialize/funcster). Registers the new analyzer node, adds rule metadata (explanations, remediations, category, pattern names), and ships unit tests for all rules including hardened-form and language-gating negative cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Ram Dwivedi <abhiram.dwivedi@yahoo.com>
393942f to
12dff95
Compare
rng1995
left a comment
There was a problem hiding this comment.
[Automated SkillSpector Review]
Requesting changes. The AST/taint/static deserialization coverage is generally well structured, but the advertised argument-aware NumPy detector has a positional-argument bypass. Handle the third positional allow_pickle argument and add a regression before approval.
|
@AbhiramDwivedi Please address review comments and resolve merge conflicts. |
numpy.load(file, mmap_mode, allow_pickle) accepts allow_pickle as its third positional argument; AST10 only checked the allow_pickle= keyword, so numpy.load(f, None, True) bypassed the deserialization finding. Signed-off-by: Ram Dwivedi <abhiram.dwivedi@yahoo.com>
…ialization-detection Signed-off-by: Ram Dwivedi <abhiram.dwivedi@yahoo.com> # Conflicts: # src/skillspector/nodes/analyzers/behavioral_ast.py
|
Fixed. `numpy.load()` passes `allow_pickle` as its third positional parameter (`file, mmap_mode, allow_pickle, ...`), so `numpy.load(f, None, True)` bypassed AST10 since it only checked the keyword form. AST10 now also inspects `node.args[2]` when present. Added regression tests: positional `allow_pickle=True` (flagged), positional `mmap_mode` alone (not flagged), and positional `allow_pickle=False` (not flagged) — alongside the existing keyword-form tests. Also re-merged `upstream/main` to clear the conflicts (all in the shared-AST-cache refactor to `behavioral_ast.py`; resolved by keeping the new `ParsedPythonFile`-based signature while preserving the AST10 deserialization logic). Full unit suite passes (2093 passed; the 16 failures on this branch are pre-existing on `upstream/main` too — Windows-only symlink/`os.open`-mocking behavior, unrelated to this PR, verified via worktree against a clean `upstream/main` checkout). |
… node The DS1-DS4 node was written before main's inspection-ledger refactor and still called run_static_patterns, so it was the only static analyzer that reported findings without accounting for the files it inspected: it never appeared in analysis_completeness or the analyzer-status table. Switch to run_static_patterns_with_ledger, matching the other 14 static analyzers, and cover the completed/skipped work items with tests. Signed-off-by: Ram Dwivedi <abhiram.dwivedi@yahoo.com>
|
Follow-up on Switched it to Current state of the branch:
PR is |
What & why
Closes #245. Adds insecure-deserialization detection (CWE-502; OWASP ASI05 – Unexpected Code Execution). Before this change an RCE-class skill — e.g. PHP
unserialize($_GET…)— scanned SAFE / 0.Changes
behavioral_ast(AST10) —pickle/cPickle/_pickle/marshal/dill/jsonpickle/joblib/pandas.read_pickle, plus argument-awareyaml.load,torch.load,numpy.loadso hardened forms (SafeLoader,weights_only=True, defaultallow_pickle=False) are not flagged. MEDIUM / 0.70.behavioral_taint_tracking(TT6) — external or file input → deserialization sink; HIGH / 0.85 (the deserialization analogue of TT5). File-read sources are deliberately included: loading a bundled/downloaded blob is the classic skill vector.static_patterns_deserialization(DS1–DS4) — new language-gated regex module for the non-Python scripts a skill may bundle: PHPunserialize(DS1), RubyMarshal/restore(DS2), RubyYAML/Psych/Oj(DS3), JSnode-serialize/serialize-to-js/funcster(DS4). Registered in the analyzer registry; Python is intentionally excluded here (covered with AST/taint precision above).pattern_defaults.py— newInsecure Deserializationcategory plus explanation / remediation / display-name / category metadata for every new rule.Scope
Only the languages SkillSpector already supports — Python (deep), JS/TS, Ruby, PHP (breadth). Java/.NET are intentionally out of scope.
Tests / validation
test_behavioral_ast.py,test_behavioral_taint_tracking.py,test_static_patterns_deserialization.py;test_registry.pyupdated for the new node.make lintclean;ruff format --checkclean; full unit suite: 1294 passed, 15 skipped, 6 xfailed.yaml.safe_load,weights_only=True) confirmed not false-positived.All commits are DCO signed-off.