@gotgenes/pi-permission-system 40.0.0 → 40.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [40.0.1](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v40.0.0...pi-permission-system-v40.0.1) (2026-10-06)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **pi-permission-system:** a /dev/null redirect keeps the core-reader wrapper exemption ([25d8455](https://github.com/gotgenes/pi-packages/commit/25d84557eac5e09974ba1ffcb5b81fccf25e0f34))
|
|
14
|
+
|
|
8
15
|
## [40.0.0](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v39.1.2...pi-permission-system-v40.0.0) (2026-10-06)
|
|
9
16
|
|
|
10
17
|
|
package/package.json
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type TokenEffect, UNPROVEN_EFFECT } from "#src/access-intent/effect";
|
|
2
|
+
import { isDiscardDevice } from "#src/path/safe-system-paths";
|
|
2
3
|
import { redirectDestinationEffect } from "./command-effects";
|
|
3
4
|
import { parseUnresolvedAt } from "./parse-health";
|
|
4
5
|
import type { TSNode } from "./parser";
|
|
@@ -88,9 +89,15 @@ export function redirectEffectForDestination(
|
|
|
88
89
|
* are the operator and a descriptor, so the loop finds nothing to refuse on and
|
|
89
90
|
* clears the exemption for a form nobody understood (#814).
|
|
90
91
|
*
|
|
91
|
-
* Past that, only
|
|
92
|
-
* names no file
|
|
93
|
-
*
|
|
92
|
+
* Past that, only three things clear it: a descriptor duplication (`2>&1`),
|
|
93
|
+
* which names no file; a destination spelled exactly `/dev/null`, whose writes
|
|
94
|
+
* touch no file (ADR 0013 §11 withholds the exemption only for a *real* output
|
|
95
|
+
* redirect, #951); and an operator that proves a read — reading a file
|
|
96
|
+
* alongside a pure reader leaves it a pure reader.
|
|
97
|
+
*
|
|
98
|
+
* The device check reads the node's raw text, so a quoted or computed spelling
|
|
99
|
+
* stays unproven, and it answers only this refusal: the token collector's proof
|
|
100
|
+
* still attributes the device a write, which `path_write` rules match.
|
|
94
101
|
*/
|
|
95
102
|
export function redirectMayWriteFile(redirect: TSNode): boolean {
|
|
96
103
|
if (parseUnresolvedAt(redirect)) return true;
|
|
@@ -100,6 +107,8 @@ export function redirectMayWriteFile(redirect: TSNode): boolean {
|
|
|
100
107
|
if (!child?.isNamed) continue;
|
|
101
108
|
// A source or duplicated descriptor (`2`, `&1`) names no file.
|
|
102
109
|
if (DESCRIPTOR_NODE_TYPES.has(child.type)) continue;
|
|
110
|
+
// A write to the discard device touches no file.
|
|
111
|
+
if (isDiscardDevice(child.text)) continue;
|
|
103
112
|
if (redirectEffectForDestination(redirect, child)?.effect !== "read") {
|
|
104
113
|
return true;
|
|
105
114
|
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
/** The device a write discards: nothing written to it reaches any file. */
|
|
2
|
+
const DISCARD_DEVICE = "/dev/null";
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Paths that are universally safe and should never trigger external-directory checks.
|
|
3
6
|
* These are OS device files: read returns EOF or process streams, write discards or goes to process streams.
|
|
4
7
|
*/
|
|
5
8
|
export const SAFE_SYSTEM_PATHS: ReadonlySet<string> = new Set([
|
|
6
|
-
|
|
9
|
+
DISCARD_DEVICE,
|
|
7
10
|
"/dev/stdin",
|
|
8
11
|
"/dev/stdout",
|
|
9
12
|
"/dev/stderr",
|
|
@@ -16,3 +19,16 @@ export const SAFE_SYSTEM_PATHS: ReadonlySet<string> = new Set([
|
|
|
16
19
|
export function isSafeSystemPath(normalizedPath: string): boolean {
|
|
17
20
|
return SAFE_SYSTEM_PATHS.has(normalizedPath);
|
|
18
21
|
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Returns true only for the discard device, `/dev/null`, so a write to `path`
|
|
25
|
+
* touches no file.
|
|
26
|
+
*
|
|
27
|
+
* Narrower than {@link isSafeSystemPath}: on Linux, `/dev/std{in,out,err}`
|
|
28
|
+
* link to `/proc/self/fd/N`, and opening one for writing reopens the
|
|
29
|
+
* descriptor's underlying file with `O_TRUNC` — `cat < f > /dev/stdin`
|
|
30
|
+
* truncates `f`.
|
|
31
|
+
*/
|
|
32
|
+
export function isDiscardDevice(path: string): boolean {
|
|
33
|
+
return path === DISCARD_DEVICE;
|
|
34
|
+
}
|