Remediate RC6 container scan findings
GeoIntel release gates / Compile, test, contracts and builds (push) Canceled after 0s
GeoIntel release gates / Python and npm vulnerability policy (push) Canceled after 0s
GeoIntel release gates / GIS image, SBOM and container scan (push) Canceled after 0s

This commit is contained in:
Codex
2026-07-18 04:28:46 +02:00
parent 027e4b078b
commit 6a22fcd1f8
8 changed files with 65 additions and 2 deletions
+14
View File
@@ -101,4 +101,18 @@ def test_release_image_uses_locked_non_ai_dependencies_and_npm_ci() -> None:
assert "pip install --no-cache-dir --require-hashes -r requirements-runtime.lock" in dockerfile assert "pip install --no-cache-dir --require-hashes -r requirements-runtime.lock" in dockerfile
assert "ARG GEOINTEL_ULTRALYTICS_VERSION=8.4.99" in dockerfile assert "ARG GEOINTEL_ULTRALYTICS_VERSION=8.4.99" in dockerfile
assert '"ultralytics==$GEOINTEL_ULTRALYTICS_VERSION"' in dockerfile assert '"ultralytics==$GEOINTEL_ULTRALYTICS_VERSION"' in dockerfile
assert "ARG GEOINTEL_SETUPTOOLS_VERSION=83.0.0" in dockerfile
assert "ARG GEOINTEL_WHEEL_VERSION=0.47.0" in dockerfile
assert "COPY deploy/unraid/gosu-setpriv /usr/local/bin/gosu" in dockerfile
assert "&& pip check" in dockerfile assert "&& pip check" in dockerfile
def test_gosu_compatibility_wrapper_uses_exec_and_setpriv() -> None:
wrapper = read("deploy/unraid/gosu-setpriv")
readiness = read("scripts/run_readiness_check.sh")
assert "exec setpriv" in wrapper
assert '--reuid="$target_user"' in wrapper
assert '--regid="$target_user"' in wrapper
assert "--init-groups" in wrapper
assert "bash -n deploy/unraid/gosu-setpriv" in readiness
+7 -1
View File
@@ -13,6 +13,8 @@ ARG GEOINTEL_TORCH_INDEX_URL=https://download.pytorch.org/whl/cpu
ARG GEOINTEL_TORCH_VERSION=2.13.0 ARG GEOINTEL_TORCH_VERSION=2.13.0
ARG GEOINTEL_TORCHVISION_VERSION=0.28.0 ARG GEOINTEL_TORCHVISION_VERSION=0.28.0
ARG GEOINTEL_ULTRALYTICS_VERSION=8.4.99 ARG GEOINTEL_ULTRALYTICS_VERSION=8.4.99
ARG GEOINTEL_SETUPTOOLS_VERSION=83.0.0
ARG GEOINTEL_WHEEL_VERSION=0.47.0
ENV GEOINTEL_ENV=production \ ENV GEOINTEL_ENV=production \
GEOINTEL_API_PREFIX=/api/v1 \ GEOINTEL_API_PREFIX=/api/v1 \
@@ -48,6 +50,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
WORKDIR /app WORKDIR /app
COPY deploy/unraid/gosu-setpriv /usr/local/bin/gosu
COPY backend/pyproject.toml /app/ COPY backend/pyproject.toml /app/
COPY backend/requirements-runtime.lock /app/ COPY backend/requirements-runtime.lock /app/
COPY backend/app/__init__.py /app/app/__init__.py COPY backend/app/__init__.py /app/app/__init__.py
@@ -63,6 +66,9 @@ RUN printf '# GeoIntel backend package metadata\n' > /app/README.md \
&& pip install --no-cache-dir \ && pip install --no-cache-dir \
"ultralytics==$GEOINTEL_ULTRALYTICS_VERSION"; \ "ultralytics==$GEOINTEL_ULTRALYTICS_VERSION"; \
fi \ fi \
&& pip install --no-cache-dir \
"setuptools==$GEOINTEL_SETUPTOOLS_VERSION" \
"wheel==$GEOINTEL_WHEEL_VERSION" \
&& pip check && pip check
COPY backend/ /app/ COPY backend/ /app/
@@ -139,7 +145,7 @@ COPY deploy/unraid/nginx-all-in-one.conf /etc/nginx/conf.d/default.conf
COPY deploy/unraid/all-in-one-start.sh /usr/local/bin/geointel-all-in-one-start COPY deploy/unraid/all-in-one-start.sh /usr/local/bin/geointel-all-in-one-start
COPY --from=frontend-build /frontend/dist/ /usr/share/nginx/html/ COPY --from=frontend-build /frontend/dist/ /usr/share/nginx/html/
RUN chmod +x /usr/local/bin/geointel-all-in-one-start \ RUN chmod +x /usr/local/bin/geointel-all-in-one-start /usr/local/bin/gosu \
&& chmod +x \ && chmod +x \
/app/scripts/train_operator_yolo_detector.sh \ /app/scripts/train_operator_yolo_detector.sh \
/app/scripts/verify_real_data_detection_qa_workflow.sh \ /app/scripts/verify_real_data_detection_qa_workflow.sh \
+15
View File
@@ -0,0 +1,15 @@
#!/bin/sh
set -eu
if [ "$#" -lt 2 ]; then
echo "usage: gosu user command [args...]" >&2
exit 64
fi
target_user="$1"
shift
exec setpriv \
--reuid="$target_user" \
--regid="$target_user" \
--init-groups \
"$@"
+4 -1
View File
@@ -73,12 +73,15 @@ The dependency job:
- publishes both unfiltered and policy-filtered Python JSON reports plus the - publishes both unfiltered and policy-filtered Python JSON reports plus the
npm JSON report, including on failure. npm JSON report, including on failure.
The only current Python exceptions are the Starlette 2026 advisories recorded The only current Python/container exceptions are the Starlette 2026 advisories recorded
in `security/pip-audit-exceptions.json`. FastAPI 0.139.2 still constrains in `security/pip-audit-exceptions.json`. FastAPI 0.139.2 still constrains
Starlette below 0.53 while patched releases begin at 1.x. GeoIntel applies Starlette below 0.53 while patched releases begin at 1.x. GeoIntel applies
request-target, form-content, route-class and Linux-runtime compensating request-target, form-content, route-class and Linux-runtime compensating
controls. The exception file has a mandatory review date; readiness and CI controls. The exception file has a mandatory review date; readiness and CI
fail automatically after it expires. New advisories are never auto-ignored. fail automatically after it expires. New advisories are never auto-ignored.
The all-in-one image replaces the Go-based base-image `gosu` helper with a
small `setpriv` exec wrapper and upgrades packaged setuptools/wheel metadata;
these scanner findings are fixed rather than excepted.
The container job builds a non-AI all-in-one image and uses digest-pinned The container job builds a non-AI all-in-one image and uses digest-pinned
scanner images: scanner images:
+1
View File
@@ -118,6 +118,7 @@ bash -n scripts/deploy_tower.sh
bash -n scripts/verify_release_fresh_install.sh bash -n scripts/verify_release_fresh_install.sh
bash -n scripts/verify_release_upgrade_smoke.sh bash -n scripts/verify_release_upgrade_smoke.sh
bash -n deploy/unraid/all-in-one-start.sh bash -n deploy/unraid/all-in-one-start.sh
bash -n deploy/unraid/gosu-setpriv
bash -n deploy/unraid/run-dockerman-container.sh bash -n deploy/unraid/run-dockerman-container.sh
bash -n deploy/unraid/deploy-release.sh bash -n deploy/unraid/deploy-release.sh
bash -n deploy/unraid/rollback-dockerman-container.sh bash -n deploy/unraid/rollback-dockerman-container.sh
+7
View File
@@ -6,6 +6,9 @@ TARGET_IMAGE="${1:-geointel-ci:local}"
OUTPUT="${2:-artifacts/geointel-container-vulnerabilities.json}" OUTPUT="${2:-artifacts/geointel-container-vulnerabilities.json}"
TRIVY_IMAGE="aquasec/trivy:0.70.0@sha256:be1190afcb28352bfddc4ddeb71470835d16462af68d310f9f4bca710961a41e" TRIVY_IMAGE="aquasec/trivy:0.70.0@sha256:be1190afcb28352bfddc4ddeb71470835d16462af68d310f9f4bca710961a41e"
CACHE_DIR="${GEOINTEL_TRIVY_CACHE:-$ROOT/.cache/trivy}" CACHE_DIR="${GEOINTEL_TRIVY_CACHE:-$ROOT/.cache/trivy}"
PYTHON_CMD="${PYTHON_BIN:-python3}"
IGNORE_FILE="$(mktemp)"
trap 'rm -f "$IGNORE_FILE"' EXIT
case "$OUTPUT" in case "$OUTPUT" in
/*|*..*) /*|*..*)
@@ -16,6 +19,9 @@ esac
docker image inspect "$TARGET_IMAGE" >/dev/null docker image inspect "$TARGET_IMAGE" >/dev/null
mkdir -p "$ROOT/$(dirname "$OUTPUT")" "$CACHE_DIR" mkdir -p "$ROOT/$(dirname "$OUTPUT")" "$CACHE_DIR"
"$PYTHON_CMD" "$ROOT/scripts/verify_security_exceptions.py"
"$PYTHON_CMD" "$ROOT/scripts/verify_security_exceptions.py" \
--print-container-ids > "$IGNORE_FILE"
# Keep the complete report, including vulnerabilities without an available fix. # Keep the complete report, including vulnerabilities without an available fix.
docker run --rm \ docker run --rm \
@@ -42,6 +48,7 @@ docker run --rm \
--timeout 20m \ --timeout 20m \
--skip-version-check \ --skip-version-check \
--ignore-unfixed \ --ignore-unfixed \
--ignorefile "$IGNORE_FILE" \
--severity HIGH,CRITICAL \ --severity HIGH,CRITICAL \
--exit-code 1 \ --exit-code 1 \
"$TARGET_IMAGE" "$TARGET_IMAGE"
+15
View File
@@ -40,12 +40,23 @@ def load_and_validate() -> tuple[dict[str, object], list[str]]:
if not isinstance(item, dict) or len(str(item.get("reason", ""))) < 30: if not isinstance(item, dict) or len(str(item.get("reason", ""))) < 30:
errors.append("every advisory requires a specific reason") errors.append("every advisory requires a specific reason")
break break
aliases = [
str(alias)
for item in advisories
if isinstance(item, dict)
for alias in item.get("aliases", [])
]
if len(aliases) != len(set(aliases)) or any(
not alias.startswith("CVE-") for alias in aliases
):
errors.append("container aliases must be unique CVE identifiers")
return payload, errors return payload, errors
def main() -> int: def main() -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--print-ids", action="store_true") parser.add_argument("--print-ids", action="store_true")
parser.add_argument("--print-container-ids", action="store_true")
args = parser.parse_args() args = parser.parse_args()
payload, errors = load_and_validate() payload, errors = load_and_validate()
if errors: if errors:
@@ -55,6 +66,10 @@ def main() -> int:
if args.print_ids: if args.print_ids:
for item in payload["advisories"]: for item in payload["advisories"]:
print(item["id"]) print(item["id"])
elif args.print_container_ids:
for item in payload["advisories"]:
for alias in item.get("aliases", []):
print(alias)
else: else:
print( print(
"Dependency exceptions valid through " "Dependency exceptions valid through "
+2
View File
@@ -19,6 +19,7 @@
}, },
{ {
"id": "PYSEC-2026-249", "id": "PYSEC-2026-249",
"aliases": ["CVE-2026-54283"],
"reason": "GeoIntel rejects application/x-www-form-urlencoded before Starlette form parsing." "reason": "GeoIntel rejects application/x-www-form-urlencoded before Starlette form parsing."
}, },
{ {
@@ -27,6 +28,7 @@
}, },
{ {
"id": "PYSEC-2026-2281", "id": "PYSEC-2026-2281",
"aliases": ["CVE-2026-48818"],
"reason": "The supported all-in-one production runtime is Linux; the advisory affects Windows StaticFiles." "reason": "The supported all-in-one production runtime is Linux; the advisory affects Windows StaticFiles."
} }
] ]