URL uses gitea@ (Gitea built-in SSH server validates against the
OS user 'gitea', not 'git'). Mirrors scripts/lib/metablog-ingest-site.sh
from sub-project B (PR #97) with the streamlit- prefix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two bug fixes to the CMSD-1.0 license-header tool:
1. detect_existing() previously matched the SPDX token anywhere in the
first 10 lines, including inside docstrings and prose comments.
Tightened the matcher to require comment markers (#, //, *, <!--)
and whitespace before the SPDX token.
Regression tests:
- test_detect_existing_no_false_match_in_docstring
- test_detect_existing_no_false_match_inline_comment_prose
2. _read_enrollment() now returns ["**"] when the allowlist file is
absent, per spec §5.2 "missing file → repo-wide enforcement".
Previously it returned [] (nothing enforced), making Phase C closure
impossible without an additional file.
Empty allowlist file still returns [] (Phase A initial), so
test_main_empty_allowlist_passes_check is unaffected.
New tests:
- test_read_enrollment_missing_file_means_repo_wide
- test_main_check_missing_allowlist_enforces_repo_wide
Suite: 49 → 53 passing. --check still exits 0 repo-wide.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A merge from feature/83 reverted the fix from commit 22014865, which had
already replaced `haproxyctl regen` (non-existent sub-command, printed
help text and exited 0) with the correct `haproxyctl generate`.
This commit re-applies the same fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Discovered during Phase B pilot: running `--fix packages/secubox-hub`
yielded zero files because walk() computed `rel` relative to the walk
root (the subdir) while allowlist patterns are repo-relative.
Add optional `repo_root` parameter to walk(); when present, allowlist
matching uses paths relative to it instead of the walk root.
Backwards-compatible (defaults to None, preserving existing tests).
main() passes the discovered repo_root through.
Regression test added: test_walk_subdir_with_repo_root_arg.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After Session 156 fix, secubox-haproxy.service was safely restarted but
cpf.gk2.secubox.in (and other streamlit instances) immediately fell back
to "Wrong Domain" because sync-mitmproxy-routes.sh was clobbering their
container routes.
Two compounding bugs:
1. DEAD_CONTAINER_IPS included 10.100.0.50 — but that's the streamlit
LXC's actual IP (RUNNING). fix_dead_container_routes() rewrote every
route pointing to 10.100.0.50 (cpf:8523, etc.) to 10.100.0.1:9080.
2. The update conditional reapplied routes when the existing port didn't
match the computed port. The computed port logic only knows metablog
vs webui — it has no streamlit awareness, so it always tried to force
streamlit domains to port 9080 (webui default).
Fixes:
- Drop 10.100.0.50 from DEAD_CONTAINER_IPS. The streamlit LXC at that
address is alive; routes pointing to it are valid.
- Change "update if existing missing OR port mismatch" to "update only
if existing is missing." The script now only ADDS routes for ACL/
metablog domains; it never overwrites existing routes set by other
sources (sync-all-routes.sh, manual edits, etc.). fix_dead_container_
routes() still handles truly dead IPs separately.
Verified: cpf.gk2.secubox.in → HTTP 200 "Streamlit" persists across
sync-mitmproxy-routes.service runs. arm/admin still 200 with correct
content.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Triggered by cpf.gk2.secubox.in returning "Wrong Domain". Root cause: the
secubox-haproxy generators (both bash haproxyctl and Python FastAPI) emit
config rules that point to dead/wrong backends, leading to HTTPS-wide
metablog/streamlit outages when a regen runs.
Generator fixes:
- packages/secubox-haproxy/api/main.py: use_backend waf_inspector
→ use_backend mitmproxy_inspector (2 occurrences). waf_inspector
pointed to 127.0.0.1:8890 which is not listening; mitmproxy_inspector
is the actual healthy WAF backend at 10.100.0.60:8080.
- packages/secubox-haproxy/sbin/haproxyctl: default_backend fallback
→ default_backend mitmproxy_inspector (2 occurrences). The fallback
backend deny_status 503's unknown hosts; switching to mitmproxy_inspector
lets mitmproxy dispatch all hosts via its routes JSON (245 routes vs
93 declared in haproxy.toml).
New safe regen wrapper:
- scripts/secubox-haproxy-regen-safe: snapshot → regen via haproxyctl →
validate via haproxy -c -f → atomic swap → reload. On validation
failure, current cfg is preserved and the broken candidate is saved
for forensics. Prevents another silent breakage like Session 156.
Board-side state (not in repo): /etc/haproxy/haproxy.cfg patched in
place to mirror these fixes, secubox-haproxy.service stopped until
user confirms safe to re-enable. All 245 routes verified live (cpf,
arm, lldh, admin, pub, werdl, 3d, 42, zkp all HTTP 200 with correct
content).
See .claude/HISTORY.md Session 156 for full symptom chain and the
6-layer root cause analysis.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses code review:
- Important #4: finish now fetches issue title via `gh issue view --json title`
and uses it as PR title (was using the branch name). Falls back to branch
name if the fetch fails or returns empty.
- Minor #8: explicit `return 4` on `git push` failure for consistency with
the documented exit-code contract.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Also patches gh-mock to sanitize dashes in branch-derived env var names,
and fixes --head two-arg parsing in the pr view mock handler.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause: log() in sync-mitmproxy-routes.sh wrote to stdout, polluting
$(fix_dead_container_routes ...) capture → corrupted JSON → jq parse
errors → set -e abort → bad routes pushed to mitmproxy container →
mitmproxy restart-loop → HAProxy backend DOWN → HTTP 503 on every
*.gk2.secubox.in metablog vhost (arm, zkp, 3d, ~160 sites).
Fixes:
- log() now writes to stderr (>&2) — preserves stdout for command-sub
- fix_dead_container_routes returns 0 (was returning $fixed count, set -e
killed the caller's assignment)
- sync-all-routes.sh routes metablog domains to port 8900 (was 9080,
which is webui.conf default_server → "Wrong Domain")
- defensive 2>/dev/null || true on jq reads
- fallback to old routes_json on jq write failure (preserves last valid
state instead of crashing)
- flock guard prevents concurrent runs on /run/sync-mitmproxy-routes.lock
Verified: systemctl start sync-mitmproxy-routes.service exits 0/SUCCESS,
244 routes valid in mitmproxy container, all four spot-checked domains
(admin.gk2, arm.gk2, zkp.gk2, 3d.gk2) return HTTP 200 with correct titles.
Note: also disabled duplicate timer secubox-route-sync.timer on the
board (executed the same script, racing on same file). systemd-level
change is on board only, not in repo.
See .claude/HISTORY.md Session 153 for full symptom chain and verification.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
apt-get update may exit non-zero on unsigned/file:// warnings while
the SecuBox repo is correctly discovered. Capture the log first
(via tee, so root-owned chroot output is writeable), then grep
separately so pipefail can't mask a successful discovery.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four-step validation gate: reprepro check, gpg verify on InRelease,
license byte-match against project root, optional chroot apt-update
smoke test (SKIP_CHROOT=1 to disable).
Accepts French or English "Good signature" / "Bonne signature" so
the gate works under any locale.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Generates output/repo/{nginx-apt.conf, DEPLOY.md, install.sh,
LICENCE-CMSD-1.0.md, LICENSE-CMSD-1.0.en.md}. No network operations
- artifacts are for the user to push out-of-band.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pins GPG_HOME=~/.gnupg/secubox (persistent), invokes existing
generate-gpg-key.sh, and writes the long fingerprint to
FINGERPRINT.txt for downstream consumption by reprepro SignWith.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brainstormed design for adding the SPDX-CMSD-1.0 header to every
first-party source file (~2,170 across 6 languages), backed by a
reusable Python tool (scripts/license-headers.py), a CI check, and
a phased per-package rollout. Spec covers scope, header rendering
per language, placement rules, tool architecture, CI integration
with an enrollment allowlist, and verification steps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- scripts/sync-mitmproxy-routes.sh: Auto-sync HAProxy vhosts to mitmproxy
- Runs via systemd timer every 5 minutes
- Fixed bash arithmetic for set -e compatibility
- secubox-metablogizer: Enhanced export ZIP package
- Includes nginx.conf, haproxy.cfg, certificates
- Complete README with republishing instructions
- Site health check endpoint
- secubox-users: Added REVOKE ALL sessions panic button
- Emergency endpoint to revoke all active sessions
- Double confirmation UI for safety
- secubox-publish: Added health status columns
- HTTP/HTTPS/WAF status with emoji indicators
- Aggregated health LEDs bar
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Auto-generated health check endpoints for sidebar status:
- Returns {status: "ok", module: "name"}
- Public endpoint (no auth required)
- Used by sidebar.js for LED status display
Added via scripts/add-health-endpoints.py
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- scripts/capture-screenshots.py: Auto-discovers 117 modules from packages/
- Playwright-based capture with JWT authentication
- 30-second delay per page for cache refresh
- Thumbnail generation (400x225)
- JSON manifest output
- scripts/generate-docs.py: Extended to 105 modules
- Multilingual descriptions (EN, FR, DE, ZH)
- --include-screenshots flag for wiki integration
- Category index pages (CATEGORIES-XX.md)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Sidebar: Replace LED dots with dice icons (⚀-⚅) based on metric values
- Sidebar: Dice update dynamically with CPU/mem/disk/load/temp/net
- SOC: Add scribe trace histogram for firewall packet stats
- SOC: Remove category tag listings (keep donut metrics)
- SOC: Fix API paths for hub endpoints
- WAF: Fix JavaScript syntax errors (orphan returns, extra braces)
- WAF: Fix getSiteEmoji function structure
- HealthBump: Update LED pulse patterns (1/2/4 flashes per tier)
- HealthBump: Pulse from bright to dim (not dark to color)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>