@codyswann/lisa 4.33.12 → 4.33.13
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/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +53 -4
- package/dist/configs/vitest/scratch-setup.d.ts +34 -0
- package/dist/configs/vitest/scratch-setup.d.ts.map +1 -1
- package/dist/configs/vitest/scratch-setup.js +89 -10
- package/dist/configs/vitest/scratch-setup.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +4 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +7 -4
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/opencode/plugin-templates/parity-safety-net.sh +53 -4
- package/package.json +4 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/hooks/parity-safety-net.sh +53 -4
- package/plugins/lisa-agy/hooks/parity-safety-net.sh +53 -4
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/parity-safety-net.sh +53 -4
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/parity-safety-net.sh +53 -4
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/parity-safety-net.sh +53 -4
- package/typescript/copy-contents/.husky/pre-commit +10 -6
- package/typescript/copy-contents/.husky/pre-push +46 -16
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.33.
|
|
3
|
+
"version": "4.33.13",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.33.
|
|
3
|
+
"version": "4.33.13",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.33.
|
|
3
|
+
"version": "4.33.13",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.33.
|
|
3
|
+
"version": "4.33.13",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.33.
|
|
3
|
+
"version": "4.33.13",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1019,14 +1019,40 @@ readonly GIT_CMD='(^|[^[:alnum:]_-])git[[:space:]]+'"$GIT_GLOBAL_OPTS"
|
|
|
1019
1019
|
# match. The preceding-char class still excludes `.`, `/`, and `-` to keep
|
|
1020
1020
|
# `--rm`-style flags and `foo.rm` names out.
|
|
1021
1021
|
readonly RM_CMD='(^|[^[:alnum:]_./-])([[:alnum:]_./-]*/)?rm'
|
|
1022
|
+
# Where a recursive-forced flag cluster is allowed to END.
|
|
1023
|
+
#
|
|
1024
|
+
# It used to be `([[:space:]]|$)` — whitespace or end of segment — and a closing
|
|
1025
|
+
# parenthesis is neither, so a SUBSHELL-wrapped pipeline fell out of every guard
|
|
1026
|
+
# built on these two patterns while its brace-wrapped twin was caught: `{ find
|
|
1027
|
+
# .git -type d | parallel rm -rf; }` matched because the `;` supplied the
|
|
1028
|
+
# boundary, and `(find .git -type d | parallel rm -rf)` did not (issue #3494).
|
|
1029
|
+
#
|
|
1030
|
+
# So the two STATEMENT-GROUPING terminators join whitespace here, and nothing
|
|
1031
|
+
# else. The general spelling — "any character that cannot be part of a flag",
|
|
1032
|
+
# `([^[:alnum:]-]|$)` — is the more principled statement of the intent, and is
|
|
1033
|
+
# deliberately NOT used: these patterns feed the rm TARGET WALK, the guard with
|
|
1034
|
+
# this file's longest false-positive history, and it would newly match ordinary
|
|
1035
|
+
# prose punctuation (`echo "rm -rf, then …"` on the comma). Paren and semicolon
|
|
1036
|
+
# buy the brace/paren parity the defect is about and buy nothing else.
|
|
1037
|
+
#
|
|
1038
|
+
# The cluster must still END at the boundary, so `-rfoo` is not read as `-rf`.
|
|
1039
|
+
readonly RM_RF_END='([[:space:];)]|$)'
|
|
1022
1040
|
# rm invoked with BOTH a recursive and a force flag — clustered (-rf/-fr, any
|
|
1023
1041
|
# extra letters) or split, in either order. The split gate pairs ANY recursive
|
|
1024
1042
|
# form (short cluster containing r, or --recursive) with ANY force form (short
|
|
1025
1043
|
# cluster containing f, or --force), so mixed spellings like `rm -r --force /`
|
|
1026
1044
|
# and `rm --recursive -f /` cannot slip between the short/short and long/long
|
|
1027
1045
|
# alternations (PR #1976 review).
|
|
1028
|
-
|
|
1029
|
-
|
|
1046
|
+
#
|
|
1047
|
+
# The FLAG TAILS are named separately from the `rm`-anchored patterns because
|
|
1048
|
+
# the find/xargs family below needs the identical flag vocabulary against a
|
|
1049
|
+
# different verb anchor. Before #3494 it carried its own short-cluster-only copy
|
|
1050
|
+
# and therefore missed `rm --recursive --force` — a divergence that could only
|
|
1051
|
+
# happen because the spelling set was written twice.
|
|
1052
|
+
readonly RM_RF_TAIL_CLUSTER='([[:space:]]+-[[:alnum:]-]+)*[[:space:]]+(-[[:alnum:]]*r[[:alnum:]]*f|-[[:alnum:]]*f[[:alnum:]]*r)'"$RM_RF_END"
|
|
1053
|
+
readonly RM_RF_TAIL_SPLIT='(([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*r[[:alnum:]]*|--recursive)([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*f[[:alnum:]]*|--force)'"$RM_RF_END"'|([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*f[[:alnum:]]*|--force)([[:space:]][^;&|]*)?[[:space:]](-[[:alnum:]]*r[[:alnum:]]*|--recursive)'"$RM_RF_END"')'
|
|
1054
|
+
readonly RM_RF_CLUSTER="$RM_CMD$RM_RF_TAIL_CLUSTER"
|
|
1055
|
+
readonly RM_RF_SPLIT="$RM_CMD$RM_RF_TAIL_SPLIT"
|
|
1030
1056
|
|
|
1031
1057
|
# 1. Recursive forced delete (`rm -rf`) of a filesystem root, home, or top-level
|
|
1032
1058
|
# wildcard. Two gates ANDed: the statement must invoke `rm` with BOTH a
|
|
@@ -1667,8 +1693,31 @@ fi
|
|
|
1667
1693
|
if matches '(^|[^[:alnum:]_./-])find[[:space:]][^;&|]*-exec[[:space:]]+rm[[:space:]]+(-[[:alnum:]]*r[[:alnum:]]*f|-[[:alnum:]]*f[[:alnum:]]*r)([[:space:]]|$)'; then
|
|
1668
1694
|
block "find -exec rm -rf performs a recursive forced delete on unreviewed paths"
|
|
1669
1695
|
fi
|
|
1670
|
-
|
|
1671
|
-
|
|
1696
|
+
# A command that reads its operands from STDIN and runs a program on them. The
|
|
1697
|
+
# targets are therefore whatever the upstream stage emitted, which is exactly
|
|
1698
|
+
# what makes a recursive forced delete behind one unauditable.
|
|
1699
|
+
#
|
|
1700
|
+
# Three fail-opens closed here (issue #3494), all of them the same mistake —
|
|
1701
|
+
# this rule was written standalone instead of inheriting vocabulary the file
|
|
1702
|
+
# already had:
|
|
1703
|
+
#
|
|
1704
|
+
# 1. The old prefix was `(^|[^[:alnum:]_./-])xargs`, whose excluded class
|
|
1705
|
+
# contains `/`, so the character before `xargs` in `/usr/bin/xargs` was
|
|
1706
|
+
# itself disqualifying and a PATH-SPELLED invocation never matched.
|
|
1707
|
+
# `gxargs` — the GNU findutils spelling shipped by macOS package managers —
|
|
1708
|
+
# failed the same way on the alphanumeric side. RM_CMD above solved this
|
|
1709
|
+
# exact problem for `rm` by putting the boundary before the WHOLE path and
|
|
1710
|
+
# making the directory part optional; that shape is reused verbatim.
|
|
1711
|
+
# 2. `parallel` was absent. It is a drop-in `xargs` substitute reaching the
|
|
1712
|
+
# same delete on the same unaudited stdin, and guard 14 below already had
|
|
1713
|
+
# to name it in its own evidence — it was missing only here.
|
|
1714
|
+
# 3. The flag set was a short-cluster-only copy, so `rm --recursive --force`
|
|
1715
|
+
# was allowed where `rm -rf` was refused. It now uses the SAME tails as the
|
|
1716
|
+
# rm walk, which is what makes that class of divergence unrepeatable.
|
|
1717
|
+
readonly STDIN_DELETER='(^|[^[:alnum:]_./-])([[:alnum:]_./-]*/)?(g?xargs|parallel)[[:space:]]([^;&|]*[[:space:]])?rm'
|
|
1718
|
+
if matches "$STDIN_DELETER$RM_RF_TAIL_CLUSTER" \
|
|
1719
|
+
|| matches "$STDIN_DELETER$RM_RF_TAIL_SPLIT"; then
|
|
1720
|
+
block "xargs/parallel rm -rf performs a recursive forced delete on dynamic stdin input"
|
|
1672
1721
|
fi
|
|
1673
1722
|
|
|
1674
1723
|
# 12. Disk destroyers, always on (exceeds upstream, which only scans these
|
|
@@ -243,20 +243,24 @@ elif command -v gitleaks >/dev/null 2>&1; then
|
|
|
243
243
|
fi
|
|
244
244
|
echo "✅ No secrets detected"
|
|
245
245
|
else
|
|
246
|
+
# Blocks rather than warns. This branch used to print the warning below and
|
|
247
|
+
# continue with exit 0, which made "gitleaks is not installed" and "no
|
|
248
|
+
# secrets were found" the same observable: a green commit. A credential scan
|
|
249
|
+
# that cannot run has not cleared the commit, and the lint-staged preflight
|
|
250
|
+
# further down already states the rule this branch was breaking — a check
|
|
251
|
+
# that cannot RUN is not a skip and not a pass.
|
|
246
252
|
echo ""
|
|
247
|
-
echo "
|
|
253
|
+
echo "❌ Commit blocked: Gitleaks is required and is not installed."
|
|
248
254
|
echo ""
|
|
249
|
-
echo "Gitleaks
|
|
255
|
+
echo "Gitleaks is what stops secrets reaching this repository's history, and"
|
|
256
|
+
echo "without it this hook cannot tell a clean commit from an unscanned one."
|
|
250
257
|
echo ""
|
|
251
258
|
echo "To install Gitleaks:"
|
|
252
259
|
echo " macOS: brew install gitleaks"
|
|
253
260
|
echo " Windows: scoop install gitleaks # or choco install gitleaks"
|
|
254
261
|
echo " Linux: See https://github.com/gitleaks/gitleaks#installing"
|
|
255
262
|
echo ""
|
|
256
|
-
|
|
257
|
-
echo ""
|
|
258
|
-
echo "Continuing without secret scanning..."
|
|
259
|
-
echo ""
|
|
263
|
+
exit 1
|
|
260
264
|
fi
|
|
261
265
|
|
|
262
266
|
# Check if native changes require runtime version bump
|
|
@@ -88,6 +88,44 @@ if ! command -v node >/dev/null 2>&1; then
|
|
|
88
88
|
exit 1
|
|
89
89
|
fi
|
|
90
90
|
|
|
91
|
+
# jq is required too, and it is proved HERE rather than at the first step that
|
|
92
|
+
# needs it.
|
|
93
|
+
#
|
|
94
|
+
# jq decides five things in this hook, not one: the dependency-vulnerability
|
|
95
|
+
# audit below, and — through `jq -e '.scripts[...]'` — whether `lint:slow`,
|
|
96
|
+
# `knip:check`, `knip` and `test:mutation` exist at all. A false result from
|
|
97
|
+
# those four probes is indistinguishable from a project that configures none of
|
|
98
|
+
# them, so a machine without jq printed "Skipping slow lint rules (lint:slow
|
|
99
|
+
# not configured)" about a project that configures it, ran no audit at all, and
|
|
100
|
+
# pushed green.
|
|
101
|
+
#
|
|
102
|
+
# That is the failure this whole subsystem exists to prevent: a control
|
|
103
|
+
# returning success for an input it never examined. Absence of the tool and
|
|
104
|
+
# absence of findings produced the identical observable — a clean push — so
|
|
105
|
+
# nothing downstream could tell an audited push from an unaudited one, and the
|
|
106
|
+
# audit is not decoration: it blocks real pushes on real HIGH advisories.
|
|
107
|
+
#
|
|
108
|
+
# The audit block used to warn and continue here, on the reasoning that jq "may
|
|
109
|
+
# legitimately be absent". That was written when jq gated one optional audit.
|
|
110
|
+
# The honest treatment is the one node gets immediately above, and the one the
|
|
111
|
+
# pre-commit lint-staged preflight already states in as many words: a check
|
|
112
|
+
# that cannot RUN is not a skip and not a pass — it blocks.
|
|
113
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
114
|
+
echo "❌ Push blocked: jq is required and is not installed."
|
|
115
|
+
echo ""
|
|
116
|
+
echo " Without jq this hook cannot run the dependency-vulnerability audit,"
|
|
117
|
+
echo " and cannot read package.json to tell whether lint:slow, knip and"
|
|
118
|
+
echo " test:mutation are configured — so it would skip all four and report"
|
|
119
|
+
echo " a clean push it never earned."
|
|
120
|
+
echo ""
|
|
121
|
+
echo "To install jq:"
|
|
122
|
+
echo " macOS: brew install jq"
|
|
123
|
+
echo " Windows: choco install jq # or scoop install jq"
|
|
124
|
+
echo " Linux: apt-get install jq"
|
|
125
|
+
echo ""
|
|
126
|
+
exit 1
|
|
127
|
+
fi
|
|
128
|
+
|
|
91
129
|
# BEGIN: push destination guard
|
|
92
130
|
# ---------------------------------------------------------------------------
|
|
93
131
|
# Refuse a push whose RESOLVED destination is a deploy branch it was not aimed
|
|
@@ -388,20 +426,12 @@ fi
|
|
|
388
426
|
# Run security audit
|
|
389
427
|
echo "🔒 Running security audit..."
|
|
390
428
|
|
|
391
|
-
# jq is required for all audit paths (JSON config reading, npm/yarn filtering)
|
|
429
|
+
# jq is required for all audit paths (JSON config reading, npm/yarn filtering),
|
|
430
|
+
# and is proved present at the top of this hook. There is deliberately no
|
|
431
|
+
# "jq is missing, continue anyway" branch here: that branch made a missing tool
|
|
432
|
+
# and a clean audit print the same thing.
|
|
392
433
|
if lisa_gate_covers dependency-vulnerability; then
|
|
393
434
|
echo "ℹ️ Covered by the dependency-vulnerability gate; the built-in audit stands down."
|
|
394
|
-
elif ! command -v jq >/dev/null 2>&1; then
|
|
395
|
-
echo ""
|
|
396
|
-
echo "⚠️ WARNING: jq is not installed - required for security audit"
|
|
397
|
-
echo ""
|
|
398
|
-
echo "To install jq:"
|
|
399
|
-
echo " macOS: brew install jq"
|
|
400
|
-
echo " Windows: choco install jq # or scoop install jq"
|
|
401
|
-
echo " Linux: apt-get install jq"
|
|
402
|
-
echo ""
|
|
403
|
-
echo "Continuing without security audit..."
|
|
404
|
-
echo ""
|
|
405
435
|
else
|
|
406
436
|
# Load GHSA exclusion IDs from JSON config files (managed + project-local)
|
|
407
437
|
load_audit_exclusions() {
|
|
@@ -687,10 +717,10 @@ fi
|
|
|
687
717
|
# did before — slower, but never with the integration tree silently dropped,
|
|
688
718
|
# which is the failure direction that would actually cost something.
|
|
689
719
|
#
|
|
690
|
-
# Resolved with node rather than jq
|
|
691
|
-
#
|
|
692
|
-
#
|
|
693
|
-
#
|
|
720
|
+
# Resolved with node rather than jq. This used to be a real distinction — jq
|
|
721
|
+
# was optional and node was not — and it stopped being one when jq became a
|
|
722
|
+
# hard requirement at the top of this hook. Either would answer correctly now;
|
|
723
|
+
# node stays because rewriting a working probe buys nothing.
|
|
694
724
|
# The unit script is selected only when it carries LISA_COVERAGE_SCOPE=unit.
|
|
695
725
|
# That marker is not decoration: the threshold factory reads it and enforces the
|
|
696
726
|
# `unit` block, so a `test:cov:unit` without it is measured against the floor
|