Files

5.4 KiB

issue, issue_title
issue issue_title
68 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.tsclassifyTokenAsPathCandidate 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:

// 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.