feat: vendor permission system source

This commit is contained in:
云服务部-叶林立
2026-08-19 14:35:19 +08:00
parent 198584daf8
commit 410c50a3e5
809 changed files with 157793 additions and 139 deletions
@@ -0,0 +1,87 @@
---
issue: 68
issue_title: "False positive external-directory prompt when bash command contains //"
---
# Skip bare-slash tokens in bash external-directory extraction
## Problem Statement
`extractExternalPathsFromBashCommand` fires a spurious external-directory prompt whenever a bash command contains the token `//` (e.g. a JavaScript comment in a heredoc).
`classifyTokenAsPathCandidate` accepts any token starting with `/`, and `path.normalize("//")` returns `/` (root), which is always outside CWD.
The user sees a confusing prompt referencing path `/`.
## Goals
- Eliminate false-positive external-directory prompts for bare-slash tokens (`/`, `//`, `///`, etc.).
- Add regression tests covering the reproducer and edge cases.
## Non-Goals
- Rewriting the tokenizer to use a real shell parser (acknowledged as a broader limitation in the issue; deferred to a follow-up issue — see Open Questions).
- Fixing the `stripQuotedStrings` escaped-quote limitation (separate issue).
## Background
- **Permission surface**: `external_directory` (bash variant).
- **Module**: `src/external-directory.ts``classifyTokenAsPathCandidate` is the gatekeeper that decides which tokens are path candidates.
- **Tests**: `tests/bash-external-directory.test.ts` covers `extractExternalPathsFromBashCommand` with sections for absolute paths, flags, URLs, safe system paths, etc.
The fix is a single early-return guard in `classifyTokenAsPathCandidate`.
## Design Overview
Add a check before the existing `token.startsWith("/")` branch:
```typescript
// Skip bare-slash tokens (// JS comments, lone /, etc.) — they resolve to root
// and are never meaningful path arguments in practice.
if (/^\/+$/.test(token)) return null;
```
This rejects any token composed entirely of forward slashes (`/`, `//`, `///`, …).
Tokens like `/etc/hosts` still pass because they contain non-slash characters.
No config, schema, or merge-precedence changes involved.
## Module-Level Changes
| File | Change |
| --------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `src/external-directory.ts` | Add `/^\/+$/` guard in `classifyTokenAsPathCandidate` before the `startsWith("/")` branch. |
| `tests/bash-external-directory.test.ts` | Add a "bare-slash tokens" `describe` block with tests for `//`, `/`, `///`, and mixed commands. |
## TDD Order
1. **test: add failing tests for bare-slash false positives (#68)**
- Add tests in `tests/bash-external-directory.test.ts` under a new `describe("bare-slash tokens are skipped")` block:
- `//` token → empty result.
- `/` token → empty result.
- `///` token → empty result.
- `echo // hello` → empty result.
- `// comment` alongside a real external path → only the real path reported.
- Tests fail (red).
2. **feat: skip bare-slash tokens in classifyTokenAsPathCandidate (#68)**
- Add the `/^\/+$/` guard in `classifyTokenAsPathCandidate`.
- All new tests pass (green).
- Run full suite to confirm no regressions.
3. **docs: document bare-slash fix in plan retro (#68)**
- Optional: add a retro note if anything surprising surfaces during implementation.
## Risks and Mitigations
| Risk | Mitigation |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Could this silently weaken a permission? | No — bare-slash tokens resolve to `/` (root), which is never a meaningful path argument. Skipping them only removes false positives; real paths like `/etc/hosts` are unaffected because they contain non-slash characters. |
| Regex too broad? | `/^\/+$/` only matches tokens that are *entirely* slashes. Any token with a non-slash character still goes through the existing path-candidate logic. |
| Edge case: lone `/` as a real argument (e.g. `ls /`)? | `ls /` would list root. Skipping it means no external-directory prompt for root listing. This is acceptable — the bash permission gate itself still applies, and root-listing is a read-only operation. If a user wants to block `ls /` they can deny the bash command pattern. |
## Open Questions
- **Follow-up: replace regex tokenizer with a proper parser.**
OpenCode uses `web-tree-sitter` + `tree-sitter-bash` for full AST-based path extraction.
`shell-quote` (23KB, zero deps, 47M downloads) is a lighter alternative that properly handles quoting, operators, and comments.
Either would eliminate the entire class of tokenizer edge-case bugs.
File a follow-up issue after this fix lands.