OpenHands / OpenHands/software-agent-sdk
Supply-chain typosquat detection: a deterministic sibling analyzer for the security seam
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1.1k
- Forks
- 539
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 137
Description
Gap
The openhands.sdk.security seam ships strong deterministic analyzers: PatternSecurityAnalyzer (rm -rf, curl | sh, eval()) and PolicyRailSecurityAnalyzer (fetch-to-exec, raw-disk, catastrophic-delete). All of them classify a command by its shape.
None of them classify a command by the identity of the dependency it installs. An agent that runs a perfectly ordinary npm install lodahs (one keystroke off lodash) passes at LOW risk. Typosquatting is one of the most common npm supply-chain attack vectors, and an autonomous agent that fat-fingers a name, or follows a poisoned instruction, installs the wrong package with no signal to the human.
Proposal
Add a deterministic, offline SupplyChainSecurityAnalyzer as a sibling subpackage to defense_in_depth, reusing the same SecurityAnalyzerBase contract and the shared executable-content extraction/normalization helpers. It returns SecurityRisk.HIGH when a command installs (or runs via npx/bunx/dlx) a package whose name is exactly one edit (Optimal String Alignment distance) from a curated popular package, and SecurityRisk.LOW otherwise. It never hard-denies; paired with ConfirmRisky it asks the human, matching the existing confirmation model.
Properties:
- Pure stdlib, no network, no disk, no new dependencies, deterministic.
- Scans only the executable corpus (tool arguments), never reasoning text, consistent with the two-corpus design in
defense_in_depth. - Hardened against common shell evasions: sub-command splitting on newline/
;/&&/||/|/&, env-assignment and wrapper prefixes (sudo,env ...), quote stripping, global-option and value-flag skipping, scope-preserving@versionstripping. - The typosquat gate requires OSA distance == 1 and the popular name length > 4, which suppresses false positives on short names (
vuevsvu).
Scope
New files only, plus a two-line export in security/__init__.py. No behavior change to existing analyzers. Tests mirror the existing test_pattern.py structure (same make_action helper) and cover every install/runner shape, every hardening case, and false-positive guards (e.g. npm install --prefix axio lodash must not flag axio). An offline standalone example is included.
I have a working, tested implementation ready (119 new tests; the full security suite stays green; ruff + pyright clean) and proposed the same check upstream in Goose for consistency: aaif-goose/goose#9642. Happy to open the PR if this direction is welcome.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Read the existing defense_in_depth analyzers and the SecurityAnalyzerBase contract, then compare test_pattern.py and its make_action helper. Review the requested new analyzer scope and the two-line security/init.py export. Done means the specified install and runner forms, hardening cases, false-positive guards, offline example, and full security suite are covered without changing existing analyzers.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- security
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100