fix(deps): patch socket.io-parser advisory; add local CI preflight gate
Security Audit failed on a newly-published advisory, GHSA-2m8v-j782-fhvr (socket.io-parser "Zero-attachment Memory Exhaustion", high). It affected both projects transitively via socket.io / socket.io-client. Patched to 4.2.7 in both lockfiles; no direct dependency versions changed. Also adds the gate that should have caught the previous two CI failures before they reached GitHub: - scripts/ci-preflight.sh runs every gate from .github/workflows/ci.yml (frontend and backend lint/type-check/build, plus both prod-dependency audits with the same audit-ci flags and allowlist) and additionally the backend Jest suite, which CI does not currently run. It runs all gates rather than failing fast, so one invocation surfaces every problem. ~35s. - scripts/claude-push-gate.sh wires that into a Claude Code PreToolUse hook so a `git push` is refused while any gate fails, with the failure detail returned inline. Non-push commands and --dry-run pass straight through. Verified: with the fix reverted, the gate correctly denied the push and reported "backend: lint FAIL"; with it restored, all 9 gates pass and both audit-ci invocations exit 0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
23a45703a5
commit
d44a0ea8a7
Generated
+17
-17
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mikrotik-manager-backend",
|
||||
"version": "0.17.4-beta",
|
||||
"version": "0.18.1-beta",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mikrotik-manager-backend",
|
||||
"version": "0.17.4-beta",
|
||||
"version": "0.18.1-beta",
|
||||
"dependencies": {
|
||||
"@influxdata/influxdb-client": "^1.33.2",
|
||||
"@influxdata/influxdb-client-apis": "^1.35.0",
|
||||
@@ -3095,9 +3095,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz",
|
||||
"integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==",
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
|
||||
"integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4704,9 +4704,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/glob/node_modules/brace-expansion": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.3.tgz",
|
||||
"integrity": "sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
|
||||
"integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5883,9 +5883,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "3.15.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz",
|
||||
"integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==",
|
||||
"version": "3.15.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz",
|
||||
"integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -7690,9 +7690,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.6",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz",
|
||||
"integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==",
|
||||
"version": "4.2.7",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz",
|
||||
"integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
@@ -8079,9 +8079,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/test-exclude/node_modules/brace-expansion": {
|
||||
"version": "1.1.17",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.17.tgz",
|
||||
"integrity": "sha512-w+aeW/mkgM4PyRMOJCgi3fOrTm5Q8QY1OSfn2TO2iuDj3ezIHqejmuxbjfPrqUkgqRew1iqkyAn0tr0ZwHD9+w==",
|
||||
"version": "1.1.18",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
|
||||
"integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
Generated
+17
-17
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mikrotik-manager-frontend",
|
||||
"version": "0.17.4-beta",
|
||||
"version": "0.18.1-beta",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mikrotik-manager-frontend",
|
||||
"version": "0.17.4-beta",
|
||||
"version": "0.18.1-beta",
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^5.101.0",
|
||||
"@types/leaflet": "^1.9.21",
|
||||
@@ -1348,9 +1348,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz",
|
||||
"integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==",
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
|
||||
"integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2006,9 +2006,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.17",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.17.tgz",
|
||||
"integrity": "sha512-w+aeW/mkgM4PyRMOJCgi3fOrTm5Q8QY1OSfn2TO2iuDj3ezIHqejmuxbjfPrqUkgqRew1iqkyAn0tr0ZwHD9+w==",
|
||||
"version": "1.1.18",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
|
||||
"integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4249,9 +4249,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
|
||||
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
|
||||
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -4813,9 +4813,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.16",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
|
||||
"integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
|
||||
"version": "3.3.18",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
|
||||
"integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -5960,9 +5960,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.6",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz",
|
||||
"integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==",
|
||||
"version": "4.2.7",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz",
|
||||
"integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
|
||||
Executable
+64
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run the same gates GitHub Actions runs, locally, before pushing.
|
||||
#
|
||||
# Mirrors .github/workflows/ci.yml exactly (frontend lint/type-check/build,
|
||||
# backend lint/type-check/build, prod-dependency audits) and additionally runs
|
||||
# the backend Jest suite, which CI does not currently cover.
|
||||
#
|
||||
# Usage: scripts/ci-preflight.sh [--skip-audit]
|
||||
# Exit: 0 = everything CI checks would pass, 1 = at least one gate failed.
|
||||
#
|
||||
# Runs every gate rather than failing fast, so one invocation surfaces all
|
||||
# problems instead of dribbling them out one push at a time.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
SKIP_AUDIT=0
|
||||
[[ "${1:-}" == "--skip-audit" ]] && SKIP_AUDIT=1
|
||||
|
||||
FAILED=()
|
||||
run() {
|
||||
local label="$1" dir="$2"; shift 2
|
||||
printf ' %-34s' "$label"
|
||||
if (cd "$ROOT/$dir" && "$@") >/tmp/ci-preflight-out 2>&1; then
|
||||
echo "ok"
|
||||
else
|
||||
echo "FAIL"
|
||||
FAILED+=("$label")
|
||||
# Keep a trimmed excerpt so the caller can see why without re-running.
|
||||
{
|
||||
echo "----- $label -----"
|
||||
tail -25 /tmp/ci-preflight-out
|
||||
echo
|
||||
} >>/tmp/ci-preflight-failures
|
||||
fi
|
||||
}
|
||||
|
||||
: >/tmp/ci-preflight-failures
|
||||
echo "Running CI gates locally (mirrors .github/workflows/ci.yml)"
|
||||
|
||||
run "backend: lint" backend npm run lint
|
||||
run "backend: type-check" backend npx tsc --noEmit
|
||||
run "backend: build" backend npm run build
|
||||
run "backend: tests" backend npx jest --silent
|
||||
run "frontend: lint" frontend npm run lint
|
||||
run "frontend: type-check" frontend npx tsc --noEmit
|
||||
run "frontend: build" frontend npm run build
|
||||
|
||||
if [[ $SKIP_AUDIT -eq 0 ]]; then
|
||||
run "frontend: audit (prod)" frontend \
|
||||
npx --yes audit-ci@^7 --high --skip-dev --allowlist GHSA-qwww-vcr4-c8h2
|
||||
run "backend: audit (prod)" backend \
|
||||
npx --yes audit-ci@^7 --high --skip-dev
|
||||
fi
|
||||
|
||||
if [[ ${#FAILED[@]} -eq 0 ]]; then
|
||||
echo "All CI gates passed."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "FAILED: ${FAILED[*]}"
|
||||
echo "Details in /tmp/ci-preflight-failures"
|
||||
exit 1
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# Claude Code PreToolUse(Bash) hook: refuse `git push` until the local CI gates pass.
|
||||
#
|
||||
# Reads the hook payload on stdin. Non-push commands exit immediately, so the
|
||||
# overhead on ordinary Bash calls is one short script invocation.
|
||||
#
|
||||
# On failure it emits a PreToolUse deny decision, which blocks the push and hands
|
||||
# the reason back to the model so it can fix the problem instead of retrying.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
PAYLOAD="$(cat)"
|
||||
CMD="$(printf '%s' "$PAYLOAD" | jq -r '.tool_input.command // ""' 2>/dev/null)"
|
||||
|
||||
# Only gate actual pushes (covers `git push`, `git -C <dir> push`, flags, etc.).
|
||||
# Deliberately ignores --dry-run.
|
||||
if ! printf '%s' "$CMD" | grep -Eq '(^|[;&|]|\s)git(\s+-[^;&|]*)?\s+push(\s|$)'; then
|
||||
exit 0
|
||||
fi
|
||||
if printf '%s' "$CMD" | grep -q -- '--dry-run'; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
REPO="/Users/rteslow/mikrotik-manager"
|
||||
[[ -x "$REPO/scripts/ci-preflight.sh" ]] || exit 0 # nothing to enforce
|
||||
|
||||
if OUTPUT="$("$REPO/scripts/ci-preflight.sh" 2>&1)"; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
DETAIL="$(tail -60 /tmp/ci-preflight-failures 2>/dev/null)"
|
||||
python3 - "$OUTPUT" "$DETAIL" <<'PY'
|
||||
import json, sys
|
||||
summary, detail = sys.argv[1], sys.argv[2]
|
||||
reason = (
|
||||
"Push blocked: the local CI gates failed, so this would fail on GitHub too.\n\n"
|
||||
f"{summary.strip()}\n\n{detail.strip()}\n\n"
|
||||
"Fix these, re-run scripts/ci-preflight.sh until it passes, then push."
|
||||
)
|
||||
print(json.dumps({
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "PreToolUse",
|
||||
"permissionDecision": "deny",
|
||||
"permissionDecisionReason": reason,
|
||||
}
|
||||
}))
|
||||
PY
|
||||
exit 0
|
||||
Reference in New Issue
Block a user