Repository navigation
Conversation
Co-authored-by: openhands <openhands@all-hands.dev>
all-hands-bot
left a comment
There was a problem hiding this comment.
This review was posted by an AI agent (OpenHands).
Scope: this is a verify-sdk skill change (.agents/skills/verify-sdk/**) inside OpenHands/software-agent-sdk, which owns the SDK and the in-process feature map the skill maintains, so the change belongs here. No product code is touched; the analyzer itself is PR #6034.
What I verified on head 23a4613:
- Head SHA matches the requested revision; PR is open, not merged, and no longer draft.
- All check runs on this head are green, but
cross-testsis green only because itstj-actions/changed-filesfilter coverstests/**,openhands/**,pyproject.toml,uv.lock,.github/workflows/tests.yml— not.agents/**. The gate therefore reports no cross changes and the job's test step never runs on this PR. Green CI here is not evidence for the new map entry. python .agents/skills/verify-sdk/scripts/sdk_map.py checkexits 1 on this head:
{"ok": false, "files": 21, "ids": 459, "untested": 29, "problems": {"S13-security-confirmation.md": ["line 25: tests/sdk/security/test_jev_analyzer.py does not exist"]}}- The same command on the base branch (
172bf438a) exits 0 withids: 458. The failure is introduced by this PR. tests/cross/test_verify_sdk_skill.py::test_committed_map_is_valid(which assertssdk-map checkreturns 0) fails on this head and passes on the base branch:
AssertionError: {'S13-security-confirmation.md': ['line 25: tests/sdk/security/test_jev_analyzer.py does not exist']}
Why this matters: SKILL.md names sdk-map check the read-only health check that "validates the map's structure and IDs, the index counts, the baseline line, and that every path the map cites still exists", and the repository's own cross test enforces it. The PR description says the missing product unit-test path is intentional and resolves when the product lands, but the map already has a convention for that case: a sub-feature without a unit test is written Unit: **none (untested)** and counted in the untested total, which is how the other untested rows stay checkable. Citing a not-yet-existing path leaves the committed-map validation red for every checkout that does not also carry the product branch — including the base branch this PR merges into (claude/verify-sdk-skill), and main if the verify-sdk branch lands before #6034.
Non-blocking notes, not findings: the recipe's scripted and live blocks are consistent with the product analyzer's contract at #6034 head 5a94bc2 (batched {index}.{detector} Noul questions over state.actions, api_key serialized through the canonical secret context), and the S13.jev and Gotchas prose matches that implementation (commentary is sent externally, masking does not sanitize the request, NeverConfirm still executes HIGH). The PR text says the blocks passed against product 9d0f25928, which is no longer the product head; re-running against 5a94bc2 would keep the evidence current.
One actionable finding is attached inline.
🔄 CHANGES REQUESTED
| - `S13.policy-rails`: `PolicyRailSecurityAnalyzer` returns HIGH for fetch piped to a shell, raw-disk `dd`/`mkfs`, and catastrophic `rm` of `/` or home, LOW for safe commands, using the tree-sitter shell AST (quoted, path-qualified and `sh -c` nested verbs resolved; depth bound and parse errors map to UNKNOWN). Unit: `tests/sdk/security/defense_in_depth/test_policy_rails.py`, `tests/sdk/security/defense_in_depth/test_shell_semantics.py`, `tests/sdk/security/defense_in_depth/test_shell_parser_bypasses.py`, `tests/sdk/security/test_shell_ast.py`, `tests/sdk/security/test_shell_parser.py`, `tests/sdk/security/test_shell_parser_node_shapes.py`. Example: `examples/01_standalone_sdk/47_defense_in_depth_security.py`. Real-LLM: none. | ||
| - `S13.ensemble`: `EnsembleSecurityAnalyzer(analyzers=[...])` takes the maximum concrete risk, fails closed to HIGH on a child exception, ignores UNKNOWN next to a concrete verdict unless `propagate_unknown=True`, rejects an empty list, and serializes with nested children. Unit: `tests/sdk/security/defense_in_depth/test_ensemble.py`, `tests/sdk/security/defense_in_depth/test_serialization.py`. Example: `examples/01_standalone_sdk/47_defense_in_depth_security.py`. Real-LLM: none. | ||
| - `S13.toolshield`: `ToolShieldLLMSecurityAnalyzer(llm=..., history_window, safety_experiences)` asks a separate guardrail LLM about the action plus recent history, wraps untrusted fields in escaped XML, accepts only one standalone risk label (smuggled or conflicting labels give UNKNOWN); helpers detect active MCP tools and load ToolShield safety experiences. Unit: `tests/sdk/security/test_toolshield_llm_analyzer.py` (7 tests skip without the `toolshield` extra). Example: none. Real-LLM: none. | ||
| - `S13.jev`: `JevSecurityAnalyzer` sends dangerous-operation and prompt-injection Noul questions for all pending actions in one TypeSafe request. It separates executable arguments from summary/thought/plaintext reasoning, maps the maximum probability to LOW/MEDIUM/HIGH (0.2/0.5 boundaries), records both scores, uses canonical secret serialization, and rates missing credentials or malformed/incomplete responses HIGH. Unit: `tests/sdk/security/test_jev_analyzer.py`. Example: none. Real-LLM: none. |
There was a problem hiding this comment.
This entry cites Unit: tests/sdk/security/test_jev_analyzer.py, which does not exist on this head — that file is added by the product PR #6034, not here. As a result sdk-map check exits 1 (line 25: tests/sdk/security/test_jev_analyzer.py does not exist) and tests/cross/test_verify_sdk_skill.py::test_committed_map_is_valid fails; both pass on the base branch 172bf438a. CI does not catch it because the cross-tests job's changed-files filter excludes .agents/**, so its test step is skipped for this PR.
Since the map already has a convention for a sub-feature with no unit test, use Unit: **none (untested)** here (and let sdk-map check --fix-counts update the untested total in README.md), then change it to the real path once the product test lands. That keeps the committed map valid in every checkout instead of only one that also carries the product branch.
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
HUMAN:
This PR documents the upcoming Jev security analyzer. It's just an interim PR to the verify-sdk PR branch.
AGENT:
Summary
Verification-only PR into #5750; product code is #6034 and docs are OpenHands/docs#957.
Why
Exercise the Jev analyzer through public conversations without mixing verification changes into its product PR.
Issue Number
Refs #6033
How to Test
Previously recorded verification:
Reproduction commands, results, and limits
Run with the product checkout's environment:
The loopback and live blocks passed against product
9d0f25928. Existing S13 analyzer recipes passed. Crafted threat commands are always rejected; only a harmless marker executes in the ConfirmRisky scenario. Smoke testing is not benchmark/calibration evidence; a benign warning has rated MEDIUM for injection suspicion.sdk-map checkpasses in the disposable product integration checkout. In this verification-only checkout the new product unit-test path is missing until the product lands; that dependency is intentional. Without TYPESAFE_API_KEY the live block exits 3 / blocked. The runner records the actor as scripted; the second block calls real Jev. Product evidence: https://github.com/smolpaws/software-agent-sdk/blob/feat/jev-security-analyzer/.pr/jev/README.mdVideo/Screenshots
No UI change; reproduction evidence is above.
Type
Notes
Depends on product #6034. Live mode needs
TYPESAFE_API_KEY; without it the recipe is blocked. Product-dependent test paths are intentionally absent from this checkout. Smoke results are not benchmark/calibration evidence; a benign warning has rated MEDIUM.Visual explanation by Codex using HumanLayer show-me.