danielmiessler / danielmiessler/LifeOS

Capture.sh collapses eight distinct preflight exit codes to a constant 3 against a header (and an in-file comment) promising propagation

Open Beginner friendly
#2,089 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
19k
Forks
2.5k
Avg merge
8d 17h
Merged PRs (30d)
1

Description

## Version

LifeOS 7.40.4 / Interceptor

## What is broken

`Capture.sh`'s header documents its exit codes as
`0 ok; 2 bad args; 3 preflight failed (propagated); 7 target denied`. Nothing is propagated. The
call site runs the gate inside `if ! …` and returns a constant — and the comment directly above it
states the contract a second time, in the same file, one line before the code that breaks it:

```bash
# --- 2. hardened preflight gate (propagate its exit + stderr verbatim) ---
if ! bash "$SCRIPT_DIR/PreflightIsolation.sh" >&2; then
# Preflight already printed structured remediation. Do not try anyway.
exit 3
fi
```

`PreflightIsolation.sh` distinguishes eight failure classes by exit code — 2 binary missing,
3 version-parse, 4 version too low, 5 nothing connected, 6 pinned context not connected, 7 target is
a denied profile, 8 test context unset, 9 no pinned Extension. All eight arrive at a `Capture.sh`
caller as `3`. A caller cannot tell "you targeted a denied profile" (a refusal it must not retry)
from "re-pin the extension" (a fixable local state) from "the binary is missing".

`Capture.sh` also reuses `3` for an unrelated condition of its own — the pinned context missing from
the live connected set at `:176` — so the code is overloaded inside the file as well as against the
header. The gate's stderr is passed through, so a human reading the terminal sees the real reason;
a script branching on the status code cannot.

The header's own words are the contract this contradicts: *"structured remediation to stderr,
distinct non-zero exit per failure class."*

Header, in-file comment and implementation disagree:

- contract — `LifeOS/install/skills/Interceptor/Tools/Capture.sh:21` — `# Exit codes: 0 ok; 2 bad args; 3 preflight failed (propagated); 7 target denied`
- contract, second statement — `LifeOS/install/skills/Interceptor/Tools/Capture.sh:130` — `# --- 2. hardened preflight gate (propagate its exit + stderr verbatim) ---`
- writer — `LifeOS/install/skills/Interceptor/Tools/Capture.sh:131-133` — the `if ! bash …PreflightIsolation.sh; then … exit 3; fi` block
- second use of the same code — `LifeOS/install/skills/Interceptor/Tools/Capture.sh:176`
- the codes being discarded — `LifeOS/install/skills/Interceptor/Tools/PreflightIsolation.sh:36-39` (documented) and their `exit` sites throughout that file

## Where (file:line)

`LifeOS/install/skills/Interceptor/Tools/Capture.sh:133`

## Repro on a clean tree

```shell
# Three different preflight failure classes, driven through both scripts. A stub
# `interceptor` satisfies the binary and connection checks; HOME is redirected so
# no real preferences file is read.

git clone --branch v7.40.4 --depth 1 https://github.com/danielmiessler/LifeOS.git /tmp/lifeos-7404
cd /tmp/lifeos-7404 && git rev-parse HEAD
# → be9e8ef889f00a29f4fd677dee4772fdf32e07ce

mkdir -p /tmp/cap/bin /tmp/cap/home /tmp/cap/ext
printf '{"version":"1.0.0"}\n' > /tmp/cap/ext/manifest.json
cat > /tmp/cap/bin/interceptor <<'STUB'
#!/usr/bin/env bash
case "$1" in
--version) echo "interceptor 0.16.9 (deadbee, 2026-08-14)" ;;
contexts) echo "[stub] → contexts"; echo "$STUB_CONTEXTS" ;;
*) exit 1 ;;
esac
STUB
chmod +x /tmp/cap/bin/interceptor

D=/tmp/lifeos-7404/LifeOS/install/skills/Interceptor/Tools
probe() { # label live-ctx pinned-ctx deny-list ext-dir
env -i HOME=/tmp/cap/home PATH=/tmp/cap/bin:/usr/bin:/bin STUB_CONTEXTS="$2" \
INTERCEPTOR_TEST_CONTEXT_ID="$3" INTERCEPTOR_WORKING_PROFILE_IDS="$4" \
INTERCEPTOR_EXT_DIR="$5" bash "$D/PreflightIsolation.sh" >/dev/null 2>&1; p=$?
env -i HOME=/tmp/cap/home PATH=/tmp/cap/bin:/usr/bin:/bin STUB_CONTEXTS="$2" \
INTERCEPTOR_TEST_CONTEXT_ID="$3" INTERCEPTOR_WORKING_PROFILE_IDS="$4" \
INTERCEPTOR_EXT_DIR="$5" bash "$D/Capture.sh" "https://example.com" >/dev/null 2>&1; c=$?
printf '%-32s preflight=%-2s Capture.sh=%-2s\n' "$1" "$p" "$c"
}
probe "target denied" 'ctx-a-9' 'ctx-a-9' 'ctx-a-9' /tmp/cap/ext
probe "pinned ctx not connected" 'ctx-live' 'ctx-stale' '' /tmp/cap/ext
probe "no Extension at all" 'ctx-a' 'ctx-a' '' /tmp/cap/nope
# → target denied preflight=7 Capture.sh=3
# → pinned ctx not connected preflight=6 Capture.sh=3
# → no Extension at all preflight=9 Capture.sh=3
```

## Negative control

The left column of that same table is the control, and it is red without reference to any fix: the
three distinct codes **exist** and the gate emits them on the same three runs, so `3` is not the
only value available — it is a value chosen after a more specific one was produced and discarded.
The security-relevant class is the first row: a refusal the caller must never retry is delivered
under the same code as a re-pin-and-try-again condition.

`Capture.sh` propagates its own codes normally, so the collapse is specific to this call site and
not a limitation of the script:

```shell
env -i HOME=/tmp/cap/home PATH=/tmp/cap/bin:/usr/bin:/bin bash "$D/Capture.sh" >/dev/null 2>&1
echo "no-args EXIT=$?"
```

```
no-args EXIT=2
```

## Suggested fix

Shape only, **untested**: capture the gate's status and re-exit with it —

```bash
bash "$SCRIPT_DIR/PreflightIsolation.sh" >&2 || exit $?
```

That makes the header's "(propagated)" true and costs one line. It does collide with `Capture.sh`'s
own use of `3` at `:176` and its own `7`, so if you would rather keep `Capture.sh`'s codes
independent, the alternative is an offset (say `20 + preflight_status`) documented in the header.
Either way the header and the code should agree; today they do not, and the header is the part
callers read.

## Before submitting

- [x] I searched open and closed issues for this defect.
Searched `Capture.sh`, `Capture.sh exit`, `Interceptor preflight isolation`. The Interceptor
issues I found — #1775, #1802, #1892, #2010, #2011, #2013, #2065 — cover other tools and
other defects; #2010 concerns a documented auto-rebind that does not exist and #2013 a
misclassified minimized window. None reports the exit-code contract. That list is what I
found, not a claim of completeness — a title search for "Interceptor" alone returns 14.
- [x] The repro runs against a clean tree of the version above, not against my modified install.
Fresh `--depth 1` clone of `v7.40.4`, `HOME` redirected, stub binary on `PATH`.
- [x] I removed personal data from the pasted output — real names, absolute home paths, tokens, my
own content. Paths are `/tmp/...`; every context id is synthetic.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with LifeOS/install/skills/Interceptor/Tools/Capture.sh at lines 21, 130-133, and 176, then compare its gate behavior with the documented and actual exit sites in PreflightIsolation.sh. Run the clean-tree reproduction for the three failure classes and verify that distinct statuses are preserved without conflicting with Capture.sh's own codes, and that the header and comment match the result.

Written by the indexing model from the issue text.

Assessment

Tech stack
shell
Domain
cli, tooling
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.