@hasna/hooks 0.4.1 → 0.5.0
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/README.md +15 -4
- package/bin/index.js +303 -48
- package/hooks/codewith-native-common.test.ts +1508 -3
- package/hooks/codewith-native-common.ts +1627 -37
- package/hooks/pre-bash/README.md +72 -2
- package/hooks/worktree-guard/README.md +9 -2
- package/package.json +1 -1
package/hooks/pre-bash/README.md
CHANGED
|
@@ -6,8 +6,78 @@ This hook is OSS-safe: optional Hasna CLIs are best-effort and missing CLIs fail
|
|
|
6
6
|
|
|
7
7
|
It also blocks scoped destructive shell operations such as recursive `rm`,
|
|
8
8
|
`rsync --delete`, destructive `find`, and destructive `git clean` / `git reset
|
|
9
|
-
--hard` forms
|
|
10
|
-
|
|
9
|
+
--hard` forms when the resolved target threatens a protected root.
|
|
10
|
+
|
|
11
|
+
## Protected roots
|
|
12
|
+
|
|
13
|
+
- `/` and the system directories (`/usr`, `/etc`, `/bin`, `/lib`, `/var`, `/boot`,
|
|
14
|
+
`/home`, `/Users`, and the other FHS and macOS equivalents). Add machine-specific
|
|
15
|
+
entries with `HASNA_PROTECTED_SYSTEM_ROOTS` (colon-separated). `/tmp` is not
|
|
16
|
+
protected — scratch cleanup there is routine.
|
|
17
|
+
- `~/.hasna`, configured workspace roots, Hasna division/scope roots, and active
|
|
18
|
+
repo/worktree roots.
|
|
19
|
+
|
|
20
|
+
These match in *root* mode: wiping a root or its contents (`rm -rf /usr`,
|
|
21
|
+
`rm -rf /usr/*`) blocks, while a targeted delete beneath one
|
|
22
|
+
(`rm -rf /usr/local/lib/my-build`) is allowed.
|
|
23
|
+
|
|
24
|
+
## Expansions that can collapse to empty
|
|
25
|
+
|
|
26
|
+
A destructive target containing a command substitution, backtick substitution or
|
|
27
|
+
variable expansion is checked twice: as written, and as the shell would render it
|
|
28
|
+
if the expansion returned empty. `rm -rf "$(anything)"/*`, `` rm -rf `cmd`/* ``,
|
|
29
|
+
`rm -rf "$VAR"/*` and `rm -rf "${VAR}"/*` are blocked by shape, whatever the
|
|
30
|
+
expansion is.
|
|
31
|
+
|
|
32
|
+
This exists because of a realized incident: `bun pm cache` exits non-zero with an
|
|
33
|
+
empty stdout when no `package.json` is found walking up from cwd, so
|
|
34
|
+
`rm -rf "$(bun pm cache)"/*` ran as `rm -rf /*`. Redirecting stderr does not help
|
|
35
|
+
— it discards the diagnostic, not the path.
|
|
36
|
+
|
|
37
|
+
Two forms are deliberately not blocked:
|
|
38
|
+
|
|
39
|
+
- `${VAR:?}` / `${VAR:?message}`, which POSIX guarantees non-empty. (`${VAR?}`
|
|
40
|
+
without the colon permits an empty value and is *not* exempt.)
|
|
41
|
+
- A bare `rm -rf "$(cmd)"` with no trailing separator, which degrades to
|
|
42
|
+
`rm -rf ""` — rejected by `rm` without deleting anything.
|
|
43
|
+
|
|
44
|
+
The recommended form is to resolve the path first and assert it:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
dir="$(bun pm cache)" || exit 1
|
|
48
|
+
case "$dir" in /|"") exit 1;; esac
|
|
49
|
+
rm -rf -- "$dir"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Globs
|
|
53
|
+
|
|
54
|
+
A glob threatens a protected root when it can match that root or an ancestor of it, or when it
|
|
55
|
+
wipes the root's contents wholesale. Matching is per path component, so a trailing literal
|
|
56
|
+
bounds the delete: `rm -rf */node_modules` at a monorepo root is allowed, while `rm -rf /*/*`
|
|
57
|
+
is not.
|
|
58
|
+
|
|
59
|
+
A glob directly under a protected root is refused only when it is *unanchored* — when no
|
|
60
|
+
literal text survives once the wildcards are removed. `[a-z]*`, `?*`, `.??*` and `*.*` are
|
|
61
|
+
unanchored and blocked; `*.log`, `tmp-*`, `.turbo*` and `snapshot-[0-9]*` keep their literal
|
|
62
|
+
anchor and are allowed.
|
|
63
|
+
|
|
64
|
+
Bracket expressions that this matcher does not model exactly — POSIX `[:class:]`, `[=equiv=]`,
|
|
65
|
+
`[.collate.]`, backslash escapes, anything unterminated — are treated as **matching**, never as
|
|
66
|
+
not-matching. An under-match would leave a protected root unmatched and allow the delete, so
|
|
67
|
+
ambiguity resolves toward refusing.
|
|
68
|
+
|
|
69
|
+
## Working directory
|
|
70
|
+
|
|
71
|
+
`cd`, `pushd`, `pushd -n`, `popd` and `cd -` are tracked, per subshell, with a directory stack.
|
|
72
|
+
A `cd` inside `( … )` or a pipeline stage applies within that shell and does not escape it.
|
|
73
|
+
|
|
74
|
+
## Wrappers
|
|
75
|
+
|
|
76
|
+
Commands are unwrapped before scanning: `bash -c` / `sh -c` / `zsh -c`, `su -c`,
|
|
77
|
+
`runuser -c`, `eval`, and `ssh host '…'`, including nested combinations. `cd` is
|
|
78
|
+
tracked within a command, and a `for VAR in <glob>` binding is followed into
|
|
79
|
+
`rm -rf "$VAR"`. Remote (`ssh`) layers only consider absolute targets, because a
|
|
80
|
+
remote relative path cannot be resolved against the local working directory.
|
|
11
81
|
|
|
12
82
|
## Install for Codewith
|
|
13
83
|
|
|
@@ -5,8 +5,15 @@ Codewith-native hook installed as `hooks run worktree-guard`.
|
|
|
5
5
|
This hook is OSS-safe: optional Hasna CLIs are best-effort and missing CLIs fail open with concise warnings. Security gates only fail closed when a guarded commit/push scan runs successfully and finds possible secrets.
|
|
6
6
|
|
|
7
7
|
It blocks scoped destructive shell operations and file-tool-like payloads when
|
|
8
|
-
the resolved target threatens
|
|
9
|
-
|
|
8
|
+
the resolved target threatens `/` or a system root (`/usr`, `/etc`, `/var`,
|
|
9
|
+
`/home`, …), `~/.hasna`, configured workspace roots, Hasna division/scope roots,
|
|
10
|
+
or active repo/worktree roots.
|
|
11
|
+
|
|
12
|
+
It shares its classifier with `pre-bash`, so it also blocks destructive targets
|
|
13
|
+
whose command substitution or variable expansion could collapse to empty —
|
|
14
|
+
`rm -rf "$(cmd)"/*` and `rm -rf "$VAR"/*`. See
|
|
15
|
+
[`hooks/pre-bash/README.md`](../pre-bash/README.md) for the full rules and the
|
|
16
|
+
recommended safe form.
|
|
10
17
|
|
|
11
18
|
## Canonical worktree path
|
|
12
19
|
|
package/package.json
CHANGED