bigpowers 2.79.1 → 2.80.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/.pi/package.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [2.80.0](https://github.com/danielvm-git/bigpowers/compare/v2.79.1...v2.80.0) (2026-07-23)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **e54s01:** freeze skill catalog baseline snapshot ([0df0fff](https://github.com/danielvm-git/bigpowers/commit/0df0fff86fc48794a2343ae4b2c421835bc5c58c))
|
|
7
|
+
|
|
1
8
|
## [2.79.1](https://github.com/danielvm-git/bigpowers/compare/v2.79.0...v2.79.1) (2026-07-23)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
|
Binary file
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# story: e54s01
|
|
3
|
+
# snapshot-catalog-baseline.sh — freeze the live skill catalog into a dated,
|
|
4
|
+
# immutable YAML baseline for e56's reclassification diff.
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
source "$SCRIPT_DIR/lib/skill-common.sh"
|
|
9
|
+
source "$SCRIPT_DIR/lib/python-env.sh"
|
|
10
|
+
|
|
11
|
+
resolve_repo_root
|
|
12
|
+
cd "$REPO_ROOT"
|
|
13
|
+
|
|
14
|
+
DRY_RUN=false
|
|
15
|
+
while [[ $# -gt 0 ]]; do
|
|
16
|
+
case "$1" in
|
|
17
|
+
--dry-run) DRY_RUN=true; shift ;;
|
|
18
|
+
-h|--help) echo "Usage: bash scripts/snapshot-catalog-baseline.sh [--dry-run]"; exit 0 ;;
|
|
19
|
+
*) echo "Unknown flag: $1" >&2; exit 2 ;;
|
|
20
|
+
esac
|
|
21
|
+
done
|
|
22
|
+
|
|
23
|
+
DATE_STR="$(date +%Y-%m-%d)"
|
|
24
|
+
COMMIT_HASH="$(git rev-parse HEAD 2>/dev/null || echo unknown)"
|
|
25
|
+
OUT_FILE="$REPO_ROOT/specs/tech-architecture/CATALOG-BASELINE-${DATE_STR}.yaml"
|
|
26
|
+
|
|
27
|
+
# unquote_source_desc — strip a source frontmatter's own surrounding quotes
|
|
28
|
+
# and unescape its inner \" , or re-quoting double-escapes the description.
|
|
29
|
+
unquote_source_desc() {
|
|
30
|
+
local desc="$1"
|
|
31
|
+
if [[ "$desc" == \"*\" ]]; then
|
|
32
|
+
desc="${desc#\"}"
|
|
33
|
+
desc="${desc%\"}"
|
|
34
|
+
desc="${desc//\\\"/\"}"
|
|
35
|
+
fi
|
|
36
|
+
echo "$desc"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# emit_skill_row — print one skill's YAML row to stdout; appends any missing-
|
|
40
|
+
# field warning line for that skill to $WARN_FILE (a plain file, since this
|
|
41
|
+
# function runs inside a command-substitution subshell and can't mutate the
|
|
42
|
+
# caller's shell variables).
|
|
43
|
+
emit_skill_row() {
|
|
44
|
+
local skill_md="$1" name phase model effort desc desc_escaped
|
|
45
|
+
name="$(basename "$(dirname "$skill_md")")"
|
|
46
|
+
parse_frontmatter_okf "$skill_md" || true
|
|
47
|
+
|
|
48
|
+
phase="$(awk '/^---/{f++} f==1 && /^phase:/{print; exit}' "$skill_md" | sed 's/^phase:[[:space:]]*//')"
|
|
49
|
+
model="${_PF_MODEL:-}"
|
|
50
|
+
effort="${_PF_EFFORT:-}"
|
|
51
|
+
desc="${_PF_DESCRIPTION:-}"
|
|
52
|
+
|
|
53
|
+
[[ -z "$phase" ]] && phase="null"
|
|
54
|
+
[[ -z "$model" ]] && { model="null"; echo " - \"${name}: missing model field\"" >> "$WARN_FILE"; }
|
|
55
|
+
[[ -z "$effort" ]] && { effort="null"; echo " - \"${name}: missing effort field\"" >> "$WARN_FILE"; }
|
|
56
|
+
[[ -z "$desc" ]] && desc="null"
|
|
57
|
+
|
|
58
|
+
desc="$(unquote_source_desc "$desc")"
|
|
59
|
+
desc_escaped="${desc//\"/\\\"}"
|
|
60
|
+
|
|
61
|
+
echo " - name: \"${name}\"\n path: \"skills/${name}/SKILL.md\"\n phase: ${phase}\n effort: ${effort}\n model: ${model}\n description: \"${desc_escaped}\"\n"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
WARN_FILE="$(mktemp)"
|
|
65
|
+
trap 'rm -f "$WARN_FILE"' EXIT
|
|
66
|
+
|
|
67
|
+
ROWS=""
|
|
68
|
+
COUNT=0
|
|
69
|
+
|
|
70
|
+
for skill_md in "$SKILLS_ROOT"/*/SKILL.md; do
|
|
71
|
+
[[ -f "$skill_md" ]] || continue
|
|
72
|
+
ROWS="${ROWS}$(emit_skill_row "$skill_md")"
|
|
73
|
+
COUNT=$((COUNT + 1))
|
|
74
|
+
done
|
|
75
|
+
|
|
76
|
+
WARNINGS="$(cat "$WARN_FILE")"
|
|
77
|
+
WARN_COUNT="$(grep -c '^ -' "$WARN_FILE" || true)"
|
|
78
|
+
|
|
79
|
+
if $DRY_RUN; then
|
|
80
|
+
echo "${COUNT} skills scanned"
|
|
81
|
+
echo "${WARN_COUNT} warnings"
|
|
82
|
+
[[ -n "$WARNINGS" ]] && echo -e "warnings:\n${WARNINGS}"
|
|
83
|
+
exit 0
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
mkdir -p "$REPO_ROOT/specs/tech-architecture"
|
|
87
|
+
|
|
88
|
+
{
|
|
89
|
+
echo "# CATALOG-BASELINE-${DATE_STR}.yaml"
|
|
90
|
+
echo "# frozen for e54-e59 migration; do not hand-edit"
|
|
91
|
+
echo "snapshot_date: \"${DATE_STR}\""
|
|
92
|
+
echo "commit_hash: \"${COMMIT_HASH}\""
|
|
93
|
+
echo "total_skill_count: ${COUNT}"
|
|
94
|
+
echo "warnings:"
|
|
95
|
+
if [[ -n "$WARNINGS" ]]; then
|
|
96
|
+
echo -e "$WARNINGS"
|
|
97
|
+
else
|
|
98
|
+
echo " []"
|
|
99
|
+
fi
|
|
100
|
+
echo "skills:"
|
|
101
|
+
echo -e "$ROWS"
|
|
102
|
+
} > "$OUT_FILE"
|
|
103
|
+
|
|
104
|
+
echo "Wrote ${OUT_FILE} (${COUNT} skills, ${WARN_COUNT} warnings)"
|
|
@@ -102,6 +102,48 @@ else
|
|
|
102
102
|
tt_fail "expired tombstone was not flagged"
|
|
103
103
|
fi
|
|
104
104
|
|
|
105
|
+
# --- Scenario: rejects path-traversal / invalid skill names (threat model CWE-22) ---
|
|
106
|
+
# tombstone-skill.sh exits non-zero on rejection, and this script runs under
|
|
107
|
+
# `set -o pipefail` — capture output via `|| true` first so the pipe's exit
|
|
108
|
+
# status doesn't mask the grep check below.
|
|
109
|
+
output=$(bash scripts/tombstone-skill.sh "../../etc/passwd" "target" 2>&1 || true)
|
|
110
|
+
if echo "$output" | grep -qi "not a valid skill name"; then
|
|
111
|
+
tt_pass "tombstone-skill.sh rejects a path-traversal old-name"
|
|
112
|
+
else
|
|
113
|
+
tt_fail "tombstone-skill.sh did not reject a path-traversal old-name"
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
output=$(bash scripts/tombstone-skill.sh "$FIXTURE_OLD" "../../etc/passwd" 2>&1 || true)
|
|
117
|
+
if echo "$output" | grep -qi "not a valid skill name"; then
|
|
118
|
+
tt_pass "tombstone-skill.sh rejects a path-traversal new-name"
|
|
119
|
+
else
|
|
120
|
+
tt_fail "tombstone-skill.sh did not reject a path-traversal new-name"
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
# --- Scenario: validate-tombstones.sh rejects an invalid name read back from the ledger ---
|
|
124
|
+
python3 -c "
|
|
125
|
+
import yaml
|
|
126
|
+
d = yaml.safe_load(open('$TOMBSTONES_FILE')) or {'tombstones': []}
|
|
127
|
+
d.setdefault('tombstones', []).append({
|
|
128
|
+
'old_name': '../../etc/passwd',
|
|
129
|
+
'new_name_or_merge_target': 'target',
|
|
130
|
+
'created_at_version': '0.0.0',
|
|
131
|
+
})
|
|
132
|
+
yaml.dump(d, open('$TOMBSTONES_FILE', 'w'), default_flow_style=False)
|
|
133
|
+
"
|
|
134
|
+
output=$(bash scripts/validate-tombstones.sh 2>&1 | strip_ansi || true)
|
|
135
|
+
if echo "$output" | grep -qi "FAIL.*not a valid skill name"; then
|
|
136
|
+
tt_pass "validate-tombstones.sh rejects a path-traversal name read from the ledger"
|
|
137
|
+
else
|
|
138
|
+
tt_fail "validate-tombstones.sh did not reject a path-traversal name read from the ledger"
|
|
139
|
+
fi
|
|
140
|
+
python3 -c "
|
|
141
|
+
import yaml
|
|
142
|
+
d = yaml.safe_load(open('$TOMBSTONES_FILE')) or {'tombstones': []}
|
|
143
|
+
d['tombstones'] = [t for t in d.get('tombstones', []) if t.get('old_name') != '../../etc/passwd']
|
|
144
|
+
yaml.dump(d, open('$TOMBSTONES_FILE', 'w'), default_flow_style=False)
|
|
145
|
+
"
|
|
146
|
+
|
|
105
147
|
echo "---"
|
|
106
148
|
if [[ "$FAILURES" -eq 0 ]]; then
|
|
107
149
|
echo -e "${GREEN}test-tombstone-mechanism: ALL PASS${NC}"
|