mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
feat: vendor permission system source
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
---
|
||||
issue: 122
|
||||
issue_title: "piInfrastructureReadPaths doesn't support glob patterns (**), causing confusing fallback to external_directory"
|
||||
---
|
||||
|
||||
# Glob support for `piInfrastructureReadPaths`
|
||||
|
||||
## Problem Statement
|
||||
|
||||
`piInfrastructureReadPaths` only supports exact directory prefixes via `isPathWithinDirectory()`.
|
||||
Users who configure glob patterns (e.g. `/opt/homebrew/**/@earendil-works/pi-coding-agent/**`) see silent failures — the path never matches, and reads fall through to the `external_directory` gate.
|
||||
The docs say the field "supports `~`", but even `~` expansion is missing for plain entries because `path-utils.ts` never calls `expandHomePath()`.
|
||||
|
||||
## Goals
|
||||
|
||||
- Support `*` and `?` wildcards in `piInfrastructureReadPaths` entries, using the existing `wildcardMatch()` semantics (where `*` matches any characters including `/`).
|
||||
- Fix `~` expansion for plain (non-glob) directory entries.
|
||||
- Update schema description, docs, and example config to reflect glob support.
|
||||
- Fully backward-compatible — plain directory entries keep prefix-match behavior.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Adding true globstar (`**` vs `*` distinction) — the existing wildcard matcher treats them identically, which is fine for path matching.
|
||||
- Changing the `wildcardMatch()` implementation itself.
|
||||
- Glob support for the static `piInfrastructureDirs` (computed internally, always absolute paths).
|
||||
|
||||
## Background
|
||||
|
||||
The infrastructure read bypass is wired as follows:
|
||||
|
||||
1. `PermissionSession.getInfrastructureReadPaths()` returns `config.piInfrastructureReadPaths ?? []`.
|
||||
2. `PermissionGateHandler` merges these with `session.getInfrastructureDirs()` into a single `infraDirs` array.
|
||||
3. `describeExternalDirectoryGate()` passes `infraDirs` to `isPiInfrastructureRead()`.
|
||||
4. `isPiInfrastructureRead()` iterates `infrastructureDirs` and calls `isPathWithinDirectory()` — pure prefix matching.
|
||||
|
||||
The existing `wildcardMatch()` in `src/wildcard-matcher.ts` already handles `*` (any characters), `?` (one character), and `~`/`$HOME` expansion.
|
||||
It is used by the rule evaluator for `external_directory` permission patterns, which is why the reporter's workaround with `external_directory` rules worked.
|
||||
|
||||
The original plan (archive/0048) explicitly deferred glob support as an open question: "Starting with directory prefixes (simpler).
|
||||
Globs can be added later if needed."
|
||||
This issue is that "later."
|
||||
|
||||
## Design Overview
|
||||
|
||||
The change is contained in `isPiInfrastructureRead()`.
|
||||
For each entry in `infrastructureDirs`, detect whether it contains glob characters (`*` or `?`).
|
||||
If it does, use `wildcardMatch(entry, normalizedPath)`.
|
||||
If it does not, expand `~` via `expandHomePath()` and use the existing `isPathWithinDirectory()` prefix match.
|
||||
|
||||
```typescript
|
||||
import { expandHomePath } from "./expand-home";
|
||||
import { wildcardMatch } from "./wildcard-matcher";
|
||||
|
||||
function containsGlobChars(value: string): boolean {
|
||||
return value.includes("*") || value.includes("?");
|
||||
}
|
||||
|
||||
// Inside isPiInfrastructureRead, the loop becomes:
|
||||
for (const dir of infrastructureDirs) {
|
||||
if (containsGlobChars(dir)) {
|
||||
if (wildcardMatch(dir, normalizedPath)) return true;
|
||||
} else {
|
||||
if (isPathWithinDirectory(normalizedPath, expandHomePath(dir))) return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is backward-compatible: entries without glob characters behave identically (prefix match), with the bonus that `~` now expands correctly.
|
||||
Entries with glob characters get full wildcard matching.
|
||||
|
||||
The `containsGlobChars` helper is a private function — not exported, no new module.
|
||||
|
||||
## Module-Level Changes
|
||||
|
||||
| File | Change |
|
||||
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `src/path-utils.ts` | Import `expandHomePath` and `wildcardMatch`. Add private `containsGlobChars()`. Update the loop in `isPiInfrastructureRead()` to branch on glob detection. |
|
||||
| `schemas/permissions.schema.json` | Update `piInfrastructureReadPaths` description: remove "no globs" caveat, document `*` and `?` support. |
|
||||
| `docs/configuration.md` | Update the `piInfrastructureReadPaths` row to mention wildcard support and add an example. |
|
||||
| `tests/pi-infrastructure-read.test.ts` | Add tests for glob patterns matching, glob patterns not matching, `~` expansion in plain entries, and mixed glob + plain entries. |
|
||||
| `tests/path-utils.test.ts` | Add corresponding `isPiInfrastructureRead` glob tests in the existing describe block. |
|
||||
|
||||
## Test Impact Analysis
|
||||
|
||||
1. The new glob branch in `isPiInfrastructureRead()` enables unit tests that were previously impossible: verifying that `*` and `?` patterns in infrastructure dirs match versioned/nested paths.
|
||||
2. No existing tests become redundant — they all exercise the plain-directory branch which is preserved.
|
||||
3. All existing `isPiInfrastructureRead` tests must stay as-is; they verify the prefix-match path still works.
|
||||
|
||||
## TDD Order
|
||||
|
||||
1. **test: glob patterns in `isPiInfrastructureRead`**
|
||||
Add failing tests to `tests/pi-infrastructure-read.test.ts`:
|
||||
- Glob entry `/opt/homebrew/*/@earendil-works/pi-coding-agent/*` matches a versioned path.
|
||||
- Glob entry with `**` behaves the same as `*` (matches across `/`).
|
||||
- Glob entry that doesn't match returns false.
|
||||
- `?` in a glob entry matches exactly one character.
|
||||
- Mixed array of plain dirs and glob patterns — both branches work.
|
||||
- Plain entry with `~` prefix now matches (currently broken).
|
||||
- Write tool with a glob-matching path is still rejected (read-only guard).
|
||||
|
||||
2. **feat: support glob patterns in `piInfrastructureReadPaths`**
|
||||
Update `src/path-utils.ts`:
|
||||
- Import `expandHomePath` from `./expand-home` and `wildcardMatch` from `./wildcard-matcher`.
|
||||
- Add private `containsGlobChars()` helper.
|
||||
- Update the `infrastructureDirs` loop in `isPiInfrastructureRead()` to branch on glob detection.
|
||||
Commit: `feat: support glob patterns in piInfrastructureReadPaths (#122)`
|
||||
|
||||
3. **test: add glob coverage to `path-utils.test.ts`** Add a few representative glob tests to the `isPiInfrastructureRead` describe block in `tests/path-utils.test.ts` to ensure both test files cover the feature.
|
||||
Commit: `test: add glob infra-read coverage to path-utils tests`
|
||||
|
||||
4. **docs: update schema and docs for glob support**
|
||||
- Update `schemas/permissions.schema.json`: change `piInfrastructureReadPaths` description to document `*`, `?`, and `~` support.
|
||||
- Update `docs/configuration.md`: revise the `piInfrastructureReadPaths` row and add a glob example.
|
||||
Commit: `docs: document piInfrastructureReadPaths glob support (#122)`
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
| Risk | Mitigation |
|
||||
| ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Overly broad glob pattern (e.g. `/*`) silently auto-allows reads everywhere | This is a user-configured field with the same trust model as `external_directory: allow` rules. The review log already records every `infrastructure_auto_allowed` bypass, making over-broad patterns visible. |
|
||||
| `wildcardMatch` treats `*` as `.*` (crosses `/`), so `*` and `**` are identical | This matches the existing wildcard semantics used everywhere else in the permission system. Document it clearly — users don't need to learn a different globbing dialect. |
|
||||
| Plain entries with `~` were silently broken before this change | Adding `expandHomePath()` to the non-glob branch fixes this as a side effect. Backward-compatible because previously `~` entries simply never matched. |
|
||||
| `containsGlobChars` false-positive on literal `*` or `?` in a path | Filesystem paths virtually never contain literal `*` or `?`. On macOS/Linux these characters are legal but extremely rare and strongly discouraged. The risk is negligible. |
|
||||
|
||||
## Open Questions
|
||||
|
||||
- None — the design reuses existing infrastructure (`wildcardMatch`, `expandHomePath`) with minimal new code.
|
||||
Reference in New Issue
Block a user