mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-02 06:54:50 +01:00
* security: isolate every gstack Bun entry point from the audited checkout's .env and bunfig.toml Bun auto-loads .env, .env.local and .env.<NODE_ENV> from its cwd and runs a cwd bunfig.toml preload before the program starts. gstack runs inside the repo it audits, so a committed .env could steer state roots, the claude binary or spend caps, and a bunfig preload ran arbitrary code in every bin and hook. Every bun shebang, shell and skill caller, TypeScript spawn and compiled build now disables both (--no-env-file --config=/dev/null, bunIsolationArgs(), --no-compile-autoload-dotenv/bunfig), with a ratchet and hostile-fixture regressions. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: name the directly executed scripts in the isolation ratchet Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: follow the isolation flags in touchfiles, copy lists, recipe assertions and the harness pin Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: keep browse/src/bun-child-config.ts unchanged so the native cookie qualification still binds Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(windows): the empty bunfig outlives its process so the timeline hook's detached repair child can read it Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * v1.91.159.0 security: gstack's Bun entry points ignore the audited repo's .env and bunfig.toml (S1) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: plan-mode render pattern accepts the isolation flags Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
155 lines
4.9 KiB
Bash
Executable File
155 lines
4.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# gstack-ios-qa-regen — deterministically regenerate the iOS DebugBridge
|
|
# package and the app-owned typed state accessors.
|
|
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: gstack-ios-qa-regen --app-source <dir> --bridge-dir <dir>
|
|
|
|
--app-source Swift source tree to scan for @Observable state
|
|
--bridge-dir Destination for the generated local DebugBridge package
|
|
EOF
|
|
}
|
|
|
|
APP_SOURCE=""
|
|
BRIDGE_DIR=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--app-source)
|
|
[[ $# -ge 2 ]] || { echo "gstack-ios-qa-regen: --app-source requires a value" >&2; exit 2; }
|
|
APP_SOURCE="$2"
|
|
shift 2
|
|
;;
|
|
--bridge-dir)
|
|
[[ $# -ge 2 ]] || { echo "gstack-ios-qa-regen: --bridge-dir requires a value" >&2; exit 2; }
|
|
BRIDGE_DIR="$2"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "gstack-ios-qa-regen: unknown argument: $1" >&2
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$APP_SOURCE" || -z "$BRIDGE_DIR" ]]; then
|
|
echo "gstack-ios-qa-regen: both --app-source and --bridge-dir are required" >&2
|
|
usage >&2
|
|
exit 2
|
|
fi
|
|
|
|
if [[ ! -d "$APP_SOURCE" ]]; then
|
|
echo "gstack-ios-qa-regen: app source directory not found: $APP_SOURCE" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v bun >/dev/null 2>&1; then
|
|
echo "gstack-ios-qa-regen: bun runtime not on PATH — install from https://bun.sh" >&2
|
|
exit 1
|
|
fi
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
GSTACK_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
TEMPLATE_DIR="$GSTACK_ROOT/ios-qa/templates"
|
|
GENERATOR="$GSTACK_ROOT/ios-qa/scripts/gen-accessors.ts"
|
|
VERSION_FILE="$GSTACK_ROOT/VERSION"
|
|
GENERATED_DIR="$APP_SOURCE/DebugBridgeGenerated"
|
|
|
|
for required in "$GENERATOR" "$VERSION_FILE"; do
|
|
if [[ ! -f "$required" ]]; then
|
|
echo "gstack-ios-qa-regen: missing required gstack file: $required" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
TMP_FILE=""
|
|
cleanup() {
|
|
if [[ -n "$TMP_FILE" ]]; then
|
|
rm -f "$TMP_FILE"
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# Copy through a sibling temporary file so interruption never leaves a
|
|
# truncated generated source. Preserve an unchanged destination byte-for-byte
|
|
# and metadata-for-metadata on repeated runs.
|
|
install_file() {
|
|
local source="$1"
|
|
local destination="$2"
|
|
|
|
if [[ ! -f "$source" ]]; then
|
|
echo "gstack-ios-qa-regen: missing template: $source" >&2
|
|
exit 1
|
|
fi
|
|
if [[ -f "$destination" ]] && cmp -s "$source" "$destination"; then
|
|
return
|
|
fi
|
|
|
|
mkdir -p "$(dirname "$destination")"
|
|
TMP_FILE="${destination}.tmp.$$"
|
|
cp "$source" "$TMP_FILE"
|
|
mv "$TMP_FILE" "$destination"
|
|
TMP_FILE=""
|
|
}
|
|
|
|
# Invalidate the completion marker before changing any package source. A
|
|
# failed or interrupted regeneration must never look current to ios-sync.
|
|
mkdir -p "$GENERATED_DIR"
|
|
rm -f -- "$GENERATED_DIR/.gstack-version"
|
|
|
|
# This is intentionally an allowlist, not a template glob. Wiring belongs to
|
|
# the consuming app and StateAccessor.swift is emitted by the parser below.
|
|
install_file "$TEMPLATE_DIR/Package.swift.template" \
|
|
"$BRIDGE_DIR/Package.swift"
|
|
install_file "$TEMPLATE_DIR/StateServer.swift.template" \
|
|
"$BRIDGE_DIR/Sources/DebugBridgeCore/StateServer.swift"
|
|
install_file "$TEMPLATE_DIR/DebugBridgeManager.swift.template" \
|
|
"$BRIDGE_DIR/Sources/DebugBridgeCore/DebugBridgeManager.swift"
|
|
install_file "$TEMPLATE_DIR/Bridges.swift.template" \
|
|
"$BRIDGE_DIR/Sources/DebugBridgeUI/Bridges.swift"
|
|
install_file "$TEMPLATE_DIR/DebugOverlay.swift.template" \
|
|
"$BRIDGE_DIR/Sources/DebugBridgeUI/DebugOverlay.swift"
|
|
install_file "$TEMPLATE_DIR/DebugBridgeTouch.m.template" \
|
|
"$BRIDGE_DIR/Sources/DebugBridgeTouch/DebugBridgeTouch.m"
|
|
install_file "$TEMPLATE_DIR/DebugBridgeTouch.h.template" \
|
|
"$BRIDGE_DIR/Sources/DebugBridgeTouch/include/DebugBridgeTouch.h"
|
|
|
|
# Older ios-sync versions copied the entire template set flat into the app's
|
|
# generated-source directory. Those files can shadow the package modules or
|
|
# make Xcode compile two harness implementations. Remove only the explicit
|
|
# obsolete generated paths; handwritten app sources are never touched.
|
|
for obsolete in \
|
|
"$BRIDGE_DIR/DebugBridgeWiring.swift" \
|
|
"$BRIDGE_DIR/StateAccessor.swift" \
|
|
"$GENERATED_DIR/Package.swift" \
|
|
"$GENERATED_DIR/StateServer.swift" \
|
|
"$GENERATED_DIR/DebugBridgeManager.swift" \
|
|
"$GENERATED_DIR/Bridges.swift" \
|
|
"$GENERATED_DIR/DebugOverlay.swift" \
|
|
"$GENERATED_DIR/DebugBridgeTouch.m" \
|
|
"$GENERATED_DIR/DebugBridgeTouch.h" \
|
|
"$GENERATED_DIR/DebugBridgeWiring.swift"
|
|
do
|
|
if [[ -f "$obsolete" || -L "$obsolete" ]]; then
|
|
rm -f -- "$obsolete"
|
|
echo "gstack-ios-qa-regen: removed obsolete generated file $obsolete"
|
|
fi
|
|
done
|
|
|
|
bun --no-env-file --config=/dev/null run "$GENERATOR" --input "$APP_SOURCE" --output "$GENERATED_DIR"
|
|
|
|
# Stamp only after successful accessor generation. ios-sync uses this marker
|
|
# to distinguish a complete current install from an interrupted regeneration.
|
|
install_file "$VERSION_FILE" "$GENERATED_DIR/.gstack-version"
|
|
|
|
echo "gstack-ios-qa-regen: bridge package ready at $BRIDGE_DIR"
|
|
echo "gstack-ios-qa-regen: accessors ready at $GENERATED_DIR/StateAccessor.swift"
|