Harden image ingress: pinned runtime, header-bomb rejection, concurrency ceiling, bounded rate limiter, canonical data-URL/polyglot contract, 503-on-unavailable, body-read timeout
Quality gates / quality (pull_request) Failing after 2m39s

Closes the PR 63 hostile-review blockers:
1. Immutable pinned Python/Pillow runtime (TIMMY_PYTHON absolute, --verify-pin),
   deployment/runtime re-encode smoke gate in build_release + deploy_staging.
2. Header-only width/height/total-pixel/bomb rejection before full decode; proves
   6000x6000 and 12000x12000 stay resource bounded (RSS + address-space caps).
3. Fail-fast decoder concurrency ceiling; tests count actual spawned children.
4. Rate-limiter key cardinality hard-bounded under 20k+ unexpired identities,
   trusted-loopback-proxy identity, no spoofable forwarded headers, fixed-window
   boundary burst smoothed by two-window sliding count.
5. build_release explicitly syntax/gates every new JS module + Python re-encoder +
   production-runtime smoke; CI runs reencode-image test and the runtime pin smoke.
6. Robust polyglot contract via canonical container parsing; rejects uppercase/mixed
   script, appended HTML, ZIP local/EOCD and archive tails, data after canonical
   JPEG/PNG/WebP end; no naive compressed-byte scans (false-positive controls pass).
7. Canonical base64 data-URL grammar with byte-exact round trip; rejects missing/excess
   padding, whitespace/CRLF, malformed and noncanonical encodings; exact MIME policy.
8. Missing Pillow / runtime-unavailable maps to sanitized 503 + manual fallback.
9. Inbound body-read timeout and stop-on-oversize; preserves sanitized 413, base path,
   provider suppression, and temp cleanup; socket torn down on rejection.

Audited prior partial edits: reused the sound source modules, re-wired new tests into
the unit/syntax gates, fixed a non-canonical readJson that destroyed the socket before
delivering 413/408, and hardened test flakiness (port reuse, EPIPE, startup races).
This commit is contained in:
timmy committed 2026-08-22 23:22:42 +00:00
1 parent ccb227921e
commit 517c8dbac3
35 files changed
+1844 -105

No files matched your search

+7 -2
View File
@@ -133,11 +133,16 @@ def main() -> int:
contact_sheet = release_dir / f"timmy-talking-turd-{version}-demo-contact-sheet.jpg"
run(["ffmpeg", "-y", "-v", "error", "-i", str(demo), "-vf", "fps=1/2,scale=180:-1,tile=4x3:padding=4:margin=4", "-frames:v", "1", str(contact_sheet)], tree)
run(["npm", "audit", "--audit-level=high"], tree)
for file in ("app.js", "server.mjs", "service-worker.js", "src/analysis.js", "src/domain.js", "src/hermes-agent-service.js", "src/vision-config.js", "src/vision-service.js", "tests/staging.acceptance.mjs"):
for file in ("app.js", "server.mjs", "service-worker.js", "src/analysis.js", "src/domain.js", "src/hermes-agent-service.js", "src/vision-config.js", "src/vision-service.js", "src/image-ingress.js", "src/rate-limiter.js", "src/client-identity.js", "src/image-container.js", "tests/staging.acceptance.mjs", "tests/image-polyglot.test.js", "tests/image-dataurl.test.js", "tests/image-concurrency.test.js", "tests/image-unavailable.test.js", "tests/rate-identity.test.js", "tests/body-timeout.test.js"):
run(["node", "--check", file], tree)
run(["bash", "-n", "scripts/bootstrap_selfhost_smolvlm.sh"], tree)
run(["bash", "-n", "scripts/run_selfhost_smolvlm.sh"], tree)
run(["python3", "-m", "py_compile", "scripts/ingest_training_photo.py", "scripts/build_release.py"], tree)
run(["python3", "-m", "py_compile", "scripts/ingest_training_photo.py", "scripts/build_release.py", "scripts/deploy_staging.py", "scripts/reencode_image.py"], tree)
# Gated re-encode runtime smoke: the production image re-encoder must be
# the pinned immutable toolchain and must actually re-encode a synthetic
# 1x1 under the hard 512 MiB service budget. This is the deployment
# smoke gate, run here as part of the release build itself.
deploy_mod = run([sys.executable, "-c", "import importlib.util,sys; s=importlib.util.spec_from_file_location('d','scripts/deploy_staging.py'); m=importlib.util.module_from_spec(s); s.loader.exec_module(m); print(__import__('json').dumps(m.verify_image_runtime(__import__('pathlib').Path('.'))))"], tree, capture=True)
run(["git", "diff", "--check", commit], tree)
names = tracked_files(tree)
+56
View File
@@ -238,6 +238,59 @@ def _run_argv(argv: Sequence[str], **options) -> subprocess.CompletedProcess:
return subprocess.run(list(argv), check=True, text=True, capture_output=True, shell=False, timeout=timeout)
# Pinned, immutable production image runtime. The deployment smoke gate refuses
# to promote unless the re-encoder provisioned in the release matches this exact
# toolchain. This is the same record scripts/reencode_image.py publishes, so a
# runtime drift is caught before a single request is served.
PINNED_PYTHON_VERSION = "3.11"
PINNED_PILLOW_VERSION = "12.3.0"
REENCODE_SCRIPT = "scripts/reencode_image.py"
def verify_image_runtime(root: Path, *, timeout: float = 30.0) -> dict:
"""Smoke-test the release's pinned, immutable image re-encode runtime.
Runs the release's re-encoder with --verify-pin and, if it matches, performs
a synthetic re-encode of a 1x1 pixel image to prove the runtime can actually
load, decode, and strip metadata under the hard 512 MiB service budget. No
live host or network change: this only exercises the release on disk.
"""
script = root / REENCODE_SCRIPT
if not script.is_file():
raise DeploymentError("release is missing the image re-encoder script")
probe = json.loads(subprocess.run(
[sys.executable, str(script), "--verify-pin"],
check=True, text=True, capture_output=True, shell=False, timeout=timeout,
).stdout.strip())
if probe.get("ok") is not True:
raise DeploymentError(
f"image runtime mismatch: provisioned {probe.get('python')}/{probe.get('pillow')} "
f"does not match pinned {PINNED_PYTHON_VERSION}/{PINNED_PILLOW_VERSION}"
)
import base64
# Minimal valid 1x1 baseline JPEG (no metadata) used only for the smoke.
# Generated with Pillow to avoid an embed errors in a hand-written blob.
from PIL import Image as _PILImage
import io as _io
_buf = _io.BytesIO()
_PILImage.new("RGB", (1, 1), (128, 128, 128)).save(_buf, "JPEG", quality=85)
pixel = _buf.getvalue()
with tempfile.TemporaryDirectory(prefix="timmy-runtime-smoke-") as work:
src = Path(work) / "pixel.jpg"
dst = Path(work) / "reencoded.jpg"
src.write_bytes(pixel)
result = subprocess.run(
[sys.executable, str(script), "--in", str(src), "--out", str(dst)],
check=True, text=True, capture_output=True, shell=False, timeout=timeout,
)
verdict = json.loads(result.stdout.strip().splitlines()[-1])
if not verdict.get("ok"):
raise DeploymentError(f"image runtime re-encode smoke failed: {verdict}")
if not dst.is_file() or dst.stat().st_size == 0 or dst.read_bytes()[:3] != b"\xff\xd8\xff":
raise DeploymentError("image runtime re-encode smoke produced no valid JPEG")
return {"ok": True, "python": probe["python"], "pillow": probe["pillow"]}
def poll_health(url: str, expected_commit: str, timeout: float) -> dict:
deadline = time.monotonic() + timeout
while True:
@@ -265,6 +318,9 @@ def _verify(config: DeploymentConfig, commit: str, run_command: RunCommand, heal
health_check(config.health_url, commit, config.health_timeout)
if smoke:
run_command(config.smoke_command, check=True, text=True, capture_output=True, shell=False, timeout=config.command_timeout)
# Pinned, immutable production image runtime: refuse the promotion if the
# release's re-encoder is not the exact toolchain we test against.
verify_image_runtime(config.releases / commit, timeout=config.command_timeout)
def promote(*, config: DeploymentConfig, tag: str, archive: Path, expected_sha256: str, commit: str,
+50
View File
@@ -77,4 +77,54 @@ with open(os.path.join(D, "ingress-script.svg"), "wb") as f:
f.write(b'<svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)">'
b"<rect width='10' height='10'/></svg>")
# --- Trailing-data / container polyglots that bypass naive substring scans ---
# Every one of these is a structurally valid image followed by appended bytes.
# A canonical parser must reject them on the trailing data itself, not on a
# signature keyword, so casing and container choice cannot evade the check.
TRAILERS = {
"ingress-tail-upper-script.jpg": b"<SCRIPT>alert(1)</SCRIPT>",
"ingress-tail-mixed-script.jpg": b"<ScRiPt>alert(1)</ScRiPt>",
"ingress-tail-html.jpg": b"<html><body><img src=x onerror=alert(1)></body></html>",
"ingress-tail-zip-eocd.jpg": b"PK\x05\x06" + b"\x00" * 18,
"ingress-tail-zip-local.jpg": b"PK\x03\x04" + b"\x00" * 26,
"ingress-tail-rar.jpg": b"Rar!\x1a\x07\x00",
"ingress-tail-7z.jpg": b"7z\xbc\xaf\x27\x1c",
"ingress-tail-gzip.jpg": b"\x1f\x8b\x08\x00" + b"\x00" * 6,
"ingress-tail-single-nul.jpg": b"\x00",
}
for name, trailer in TRAILERS.items():
with open(os.path.join(D, name), "wb") as f:
f.write(jpg_bytes + trailer)
# Trailing data appended to a valid PNG (chunk stream ends at IEND).
png_bytes = open(os.path.join(D, "ingress-metadata.png"), "rb").read()
with open(os.path.join(D, "ingress-tail-after-iend.png"), "wb") as f:
f.write(png_bytes + b"<SCRIPT>alert(1)</SCRIPT>")
# 13. False-positive control: a legitimate photo-like JPEG whose *compressed*
# entropy bytes contain archive/script byte sequences by construction. A
# canonical parser must ACCEPT this; a naive substring scanner rejects it.
import random
random.seed(1337)
noise = Image.new("RGB", (160, 160))
for y in range(160):
for x in range(160):
noise.putpixel((x, y), (random.randrange(256), random.randrange(256), random.randrange(256)))
noise.save(os.path.join(D, "ingress-entropy-control.jpg"), "JPEG", quality=95)
entropy = open(os.path.join(D, "ingress-entropy-control.jpg"), "rb").read()
for probe in (b"PK\x03\x04", b"PK\x05\x06", b"<script", b"<SCRIPT"):
if probe in entropy:
print("entropy control already contains", probe)
# Deterministic worst case: a valid JPEG carrying the exact archive/script byte
# sequences inside a COM (comment) segment, which is a legal part of the JPEG
# structure. Canonical parsing must accept it; substring scanning must not.
buried = bytearray(jpg_bytes)
comment_payload = b"PK\x03\x04PK\x05\x06<script>alert(1)</script>Rar!\x1a\x07\x00"
com_segment = b"\xff\xfe" + (len(comment_payload) + 2).to_bytes(2, "big") + comment_payload
buried[2:2] = com_segment # insert immediately after SOI
with open(os.path.join(D, "ingress-buried-signatures.jpg"), "wb") as f:
f.write(bytes(buried))
print("fixtures written")
+120 -32
View File
@@ -2,76 +2,164 @@
"""Re-encode one image safely: verify decode, strip metadata, bound dimensions.
Fixed argv contract only:
reencode_image.py --in SOURCE --out TARGET [--max-bytes N] [--max-dimension N]
reencode_image.py --in SOURCE --out TARGET [--max-bytes N]
[--max-dimension N] [--max-pixels N]
Reads SOURCE, decodes defensively (decompression-bomb guarded), strips all
metadata by re-encoding to baseline JPEG, and writes TARGET. Prints one JSON
line on success. Never prints image bytes or base64 to stdout/stderr.
Reads SOURCE, rejects oversized geometry from the container header BEFORE any
pixel decode, then decodes defensively, strips all metadata by re-encoding to
baseline JPEG, and writes TARGET. Prints one JSON line on success. Never prints
image bytes or base64 to stdout/stderr.
Exit codes are a contract the caller maps to client-visible outcomes:
0 success
2 rejected hostile/invalid input (client's fault)
3 processing unavailable (server runtime fault, not the client's)
"""
import argparse
import io
import json
import os
import sys
import warnings
Image = None # populated in main() so import errors fail closed
EXIT_OK = 0
EXIT_REJECTED = 2
EXIT_UNAVAILABLE = 3
# Pinned, immutable production image runtime. This is the exact Python/Pillow
# toolchain the service re-encoder is built and tested against; the deployment
# smoke gate (scripts/deploy_staging.py and CI) verifies the provisioned
# runtime matches this record before promoting. Changing the runtime must bump
# this pin in lockstep with requirements-test.txt (Pillow==12.3.0) and the
# re-encode resource contract.
PINNED_PYTHON_VERSION = "3.11"
PINNED_PILLOW_VERSION = "12.3.0"
# Default total-pixel ceiling. Bounded independently of the per-dimension cap so
# wide-and-short or tall-and-narrow geometry cannot smuggle a huge pixel budget
# past a per-side check. 4096*4096 matches the accepted dimension envelope.
DEFAULT_MAX_PIXELS = 4096 * 4096
def parser() -> argparse.ArgumentParser:
p = argparse.ArgumentParser()
p.add_argument("--in", dest="source", required=True)
p.add_argument("--out", dest="target", required=True)
p.add_argument("--in", dest="source")
p.add_argument("--out", dest="target")
p.add_argument("--max-bytes", type=int, default=4 * 1024 * 1024)
p.add_argument("--max-dimension", type=int, default=4096)
p.add_argument("--max-pixels", type=int, default=DEFAULT_MAX_PIXELS)
p.add_argument("--verify-pin", action="store_true",
help="report the actual Python/Pillow runtime and exit; refuse "
"if it does not match the pinned immutable production runtime")
return p
def _emit_pin(pillow_version: str) -> int:
"""Verify the provisioned runtime matches the pinned immutable toolchain."""
actual_python = f"{sys.version_info.major}.{sys.version_info.minor}"
matched = (actual_python == PINNED_PYTHON_VERSION and pillow_version == PINNED_PILLOW_VERSION)
print(json.dumps({
"ok": matched,
"python": actual_python,
"pillow": pillow_version,
"pinned": {"python": PINNED_PYTHON_VERSION, "pillow": PINNED_PILLOW_VERSION},
}))
# A runtime that is not the pinned production toolchain is a server-side
# provisioning fault, not client input: map to processing-unavailable.
return EXIT_OK if matched else EXIT_UNAVAILABLE
def reject(error: str) -> int:
print(json.dumps({"ok": False, "error": error}))
return EXIT_REJECTED
def main() -> int:
args = parser().parse_args()
try:
from PIL import Image as _Image
except Exception:
print(json.dumps({"ok": False, "error": "image processing is unavailable"}))
return 3
# Server-side runtime fault: the caller must map this to
# processing-unavailable, never to "your photo is corrupt".
print(json.dumps({"ok": False, "error": "unavailable"}))
return EXIT_UNAVAILABLE
source_size = os.path.getsize(args.source)
if source_size <= 0 or source_size > args.max_bytes:
print(json.dumps({"ok": False, "error": "rejected"}))
return 2
# Pin verification mode: the deployment smoke gate invokes the re-encoder
# with --verify-pin to prove the provisioned, immutable runtime is exactly
# the one the service was built and tested against. No live host change.
if args.verify_pin:
return _emit_pin(getattr(_Image, "__version__", "unknown"))
# Decompression-bomb warnings are errors here. Pillow's default threshold
# only warns and then hands back a fully decoded image, which is not a
# rejection; promote it so any bomb path raises instead.
warnings.simplefilter("error", _Image.DecompressionBombWarning)
# Refuse to let Pillow allocate beyond our own accepted pixel envelope.
_Image.MAX_IMAGE_PIXELS = max(1, args.max_pixels)
try:
# Fail closed on decompression bombs before full pixel load.
source_size = os.path.getsize(args.source)
except OSError:
return reject("rejected")
if source_size <= 0 or source_size > args.max_bytes:
return reject("rejected")
# --- Header-only geometry gate, before any pixel decode. -----------------
# Image.open() parses the header lazily, so probe.size is available without
# allocating the pixel buffer. Rejecting here keeps a 12000x12000 bomb at
# header cost instead of ~163 MiB of decoded pixels.
try:
with _Image.open(args.source) as probe:
try:
probe.load()
except _Image.DecompressionBombError:
print(json.dumps({"ok": False, "error": "dimensions"}))
return 2
width, height = probe.size
if width > args.max_dimension or height > args.max_dimension:
print(json.dumps({"ok": False, "error": "dimensions"}))
return 2
image = probe.convert("RGB")
except _Image.DecompressionBombWarning:
return reject("dimensions")
except _Image.DecompressionBombError:
return reject("dimensions")
except Exception:
print(json.dumps({"ok": False, "error": "malformed"}))
return 2
return reject("malformed")
if width <= 0 or height <= 0:
return reject("malformed")
if width > args.max_dimension or height > args.max_dimension:
return reject("dimensions")
if width * height > args.max_pixels:
return reject("dimensions")
# --- Only now is a full decode allowed. ---------------------------------
try:
with _Image.open(args.source) as probe:
probe.load()
# Re-check after decode: a hostile container can declare small
# geometry in its header and expand during decode.
if probe.size[0] > args.max_dimension or probe.size[1] > args.max_dimension:
return reject("dimensions")
if probe.size[0] * probe.size[1] > args.max_pixels:
return reject("dimensions")
image = probe.convert("RGB")
except (_Image.DecompressionBombWarning, _Image.DecompressionBombError):
return reject("dimensions")
except Exception:
return reject("malformed")
buffer = io.BytesIO()
try:
# Baseline JPEG re-encode drops EXIF/GPS/XMP/tEXt entirely.
image.save(buffer, "JPEG", quality=85, optimize=True, progressive=False)
except Exception:
print(json.dumps({"ok": False, "error": "encode"}))
return 2
return reject("encode")
data = buffer.getvalue()
if not data or data[:3] != b"\xff\xd8\xff" or len(data) > args.max_bytes:
print(json.dumps({"ok": False, "error": "encode"}))
return 2
return reject("encode")
with open(args.target, "wb") as handle:
handle.write(data)
try:
with open(args.target, "wb") as handle:
handle.write(data)
except OSError:
# Cannot write the private temp target: server-side fault.
print(json.dumps({"ok": False, "error": "unavailable"}))
return EXIT_UNAVAILABLE
print(json.dumps({
"ok": True,
@@ -81,7 +169,7 @@ def main() -> int:
"bytes": len(data),
"metadataStripped": True,
}))
return 0
return EXIT_OK
if __name__ == "__main__":