[MEDIUM] fetch-openapi.sh: pass $OUT via sys.argv, not f-string in heredoc #2

Closed
opened 2026-04-25 13:39:54 +02:00 by uwe.admin · 3 comments
Owner

From audit #1.

Problem

bin/fetch-openapi.sh:32-37 embeds $OUT directly into a heredoc passed to python3 -c. If OPENAPI_OUT were ever attacker-controlled (currently a dev-only env var), an unbalanced quote in the filename would allow Python code injection.

Remediation

Pass $OUT to the embedded script via sys.argv rather than shell-level f-string substitution.

Acceptance

  • No shell variables interpolated into Python source via heredoc string substitution.
  • Existing fetch-openapi.sh callers continue to work unchanged.

Effort: small.

From audit #1. ## Problem `bin/fetch-openapi.sh:32-37` embeds `$OUT` directly into a heredoc passed to `python3 -c`. If `OPENAPI_OUT` were ever attacker-controlled (currently a dev-only env var), an unbalanced quote in the filename would allow Python code injection. ## Remediation Pass `$OUT` to the embedded script via `sys.argv` rather than shell-level f-string substitution. ## Acceptance - No shell variables interpolated into Python source via heredoc string substitution. - Existing `fetch-openapi.sh` callers continue to work unchanged. Effort: small.
uwe.admin added the
new
label 2026-04-25 13:39:54 +02:00
Author
Owner

Agent Evaluation

Feasibility: Trivial. The Python heredoc is 5 lines and only uses $OUT once.

Impact: Closes the documented injection-shaped pattern. Real-world risk is low (developer-only env var) but the pattern is a footgun if the script is ever reused in CI with less-trusted inputs.

Effort: Small.

Recommendation: Accept.

Implementation plan

  1. Replace the current heredoc with a form that passes $OUT through sys.argv:
    python3 - "$OUT" <<'PY'
    import json, sys
    path = sys.argv[1]
    d = json.load(open(path))
    assert 'openapi' in d, 'not an OpenAPI spec (missing "openapi" key)'
    print(f"  openapi={d['openapi']}, paths={len(d.get('paths', {}))}")
    PY
    
    Note the 'PY' quoted heredoc delimiter — it stops the shell from doing any further interpolation inside, even if someone later adds another $VAR.
  2. No callers change (the script's contract — OPENAPI_OUT env var → file at that path — is preserved).
  3. No tests exist in this repo (#4 covers bootstrapping a vitest suite, but that's TS-side; nothing for shell). Verification is manual: run OPENAPI_OUT=/tmp/foo.json bash bin/fetch-openapi.sh against a live OpenAPI endpoint, confirm the file lands and the assertion line still prints.
## Agent Evaluation **Feasibility:** Trivial. The Python heredoc is 5 lines and only uses `$OUT` once. **Impact:** Closes the documented injection-shaped pattern. Real-world risk is low (developer-only env var) but the pattern is a footgun if the script is ever reused in CI with less-trusted inputs. **Effort:** Small. **Recommendation:** Accept. ### Implementation plan 1. Replace the current heredoc with a form that passes `$OUT` through `sys.argv`: ```sh python3 - "$OUT" <<'PY' import json, sys path = sys.argv[1] d = json.load(open(path)) assert 'openapi' in d, 'not an OpenAPI spec (missing "openapi" key)' print(f" openapi={d['openapi']}, paths={len(d.get('paths', {}))}") PY ``` Note the `'PY'` quoted heredoc delimiter — it stops the shell from doing any further interpolation inside, even if someone later adds another `$VAR`. 2. No callers change (the script's contract — `OPENAPI_OUT` env var → file at that path — is preserved). 3. No tests exist in this repo (#4 covers bootstrapping a vitest suite, but that's TS-side; nothing for shell). Verification is manual: run `OPENAPI_OUT=/tmp/foo.json bash bin/fetch-openapi.sh` against a live OpenAPI endpoint, confirm the file lands and the assertion line still prints.
uwe.admin added the
evaluated
label 2026-04-25 13:42:47 +02:00
Author
Owner

Evaluated #2 — Small, recommend accept (no decision branches; single approach).

Evaluated #2 — Small, recommend accept (no decision branches; single approach).
uwe.admin added the
accepted
label 2026-04-25 21:32:07 +02:00
Author
Owner

Implemented #2 → commit 5b0bec8 (heredoc now 'PY'-quoted, $OUT passed via sys.argv).

Implemented #2 → commit 5b0bec8 (heredoc now 'PY'-quoted, $OUT passed via sys.argv).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: uwe.admin/webapp-scaffold#2
No description provided.