Add SBOM, symbols, traces, and VEX files for CVE-2022-21661 SQLi case
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Created CycloneDX and SPDX SBOM files for both reachable and unreachable images.
- Added symbols.json detailing function entry and sink points in the WordPress code.
- Included runtime traces for function calls in both reachable and unreachable scenarios.
- Developed OpenVEX files indicating vulnerability status and justification for both cases.
- Updated README for evaluator harness to guide integration with scanner output.
This commit is contained in:
master
2025-11-08 20:53:45 +02:00
parent 515975edc5
commit 536f6249a6
837 changed files with 37279 additions and 14675 deletions

View File

@@ -46,3 +46,5 @@
| HELM-45-001 | TODO | Deployment Guild | COMPOSE-44-001 | Scaffold `deploy/helm/stella` chart with values, component toggles, and pinned image digests for all services; include migration Job templates. | Chart installs in dev cluster; images pinned; lint/tests pass. |
| HELM-45-002 | TODO | Deployment Guild, Security Guild | HELM-45-001 | Add TLS/Ingress, NetworkPolicy, PodSecurityContexts, Secrets integration (external secrets), and document security posture. | Helm values support secure defaults; policies validated; docs updated. |
| HELM-45-003 | TODO | Deployment Guild, Observability Guild | HELM-45-001 | Implement HPA, PDB, readiness gates, Prometheus scraping annotations, OTel configuration hooks, and upgrade hooks. | Rolling upgrade succeeds in CI; observability wires confirmed; upgrade docs updated. |
| HELM-45-004 | DONE (2025-11-08) | Deployment Guild, Policy Guild | HELM-45-001 | Wire Policy Engine / Gateway pods to consume the `policy-engine-activation` ConfigMap (envFrom/volume mounts), ensure host configuration loads activation overrides, and update Helm/Compose samples accordingly. | Pods mount config map deterministically; activation settings honored in Policy Engine; samples/tests updated for air-gap parity. |
> 2025-11-08: Added config builder support for `/config/policy-engine/activation.yaml`, templated envFrom injection for policy-engine/gateway pods, verified Policy Engine/Gateway tests, and CI now runs `helm lint` + `helm template` for every `values*.yaml`.

View File

@@ -45,7 +45,10 @@
| DEVOPS-AIRGAP-56-002 | TODO | DevOps Guild, AirGap Importer Guild | AIRGAP-IMP-57-002 | Provide import tooling for bundle staging: checksum validation, offline object-store loader scripts, removable media guidance. | Scripts documented; smoke tests validate import; runbook updated. |
| DEVOPS-AIRGAP-56-003 | TODO | DevOps Guild, Container Distribution Guild | EXPORT-AIRGAP-56-002 | Build Bootstrap Pack pipeline bundling images/charts, generating checksums, and publishing manifest for offline transfer. | Pipeline runs in connected env; pack verified in air-gap smoke test; manifest recorded. |
| DEVOPS-AIRGAP-57-001 | TODO | DevOps Guild, Mirror Creator Guild | MIRROR-CRT-56-002 | Automate Mirror Bundle creation jobs with dual-control approvals, artifact signing, and checksum publication. | Approval workflow enforced; CI artifact includes DSSE/TUF metadata; audit logs stored. |
| DEVOPS-AIRGAP-57-002 | TODO | DevOps Guild, Authority Guild | AUTH-OBS-50-001 | Configure sealed-mode CI tests that run services with sealed flag and ensure no egress occurs (iptables + mock DNS). | CI suite fails on attempted egress; reports remediation; documentation updated. |
| DEVOPS-AIRGAP-57-002 | DOING (2025-11-08) | DevOps Guild, Authority Guild | AUTH-OBS-50-001 | Configure sealed-mode CI tests that run services with sealed flag and ensure no egress occurs (iptables + mock DNS). | CI suite fails on attempted egress; reports remediation; documentation updated. |
> 2025-11-08: Landed `sealed-mode-compose.yml`, `run-sealed-ci.sh`, and `egress_probe.py`, plus the `.gitea/workflows/build-test-deploy.yml` job that uploads `artifacts/sealed-mode-ci/<commit>/authority-sealed-ci.json`; waiting on Authority to consume the artefact before flipping DONE.
> 2025-11-07: Blocking AUTH-AIRGAP-57-001 (Authority gating); prioritize sealed-mode CI artifacts so Authority can flip the enforcement switch.
> 2025-11-07: Target ETA agreed with Authority is 2025-11-10 for first CI run (iptables + mock DNS) plus doc updates.
| DEVOPS-AIRGAP-58-001 | TODO | DevOps Guild, Notifications Guild | NOTIFY-AIRGAP-56-002 | Provide local SMTP/syslog container templates and health checks for sealed environments; integrate into Bootstrap Pack. | Templates deployed successfully; health checks in CI; docs updated. |
| DEVOPS-AIRGAP-58-002 | TODO | DevOps Guild, Observability Guild | DEVOPS-AIRGAP-56-001, DEVOPS-OBS-51-001 | Ship sealed-mode observability stack (Prometheus/Grafana/Tempo/Loki) pre-configured with offline dashboards and no remote exporters. | Stack boots offline; dashboards available; verification script confirms zero egress. |
| DEVOPS-REL-17-004 | BLOCKED (2025-10-26) | DevOps Guild | DEVOPS-REL-17-002 | Ensure release workflow publishes `out/release/debug` (build-id tree + manifest) and fails when symbols are missing. | Release job emits debug artefacts, `mirror_debug_store.py` summary committed, warning cleared from build logs, docs updated. |

View File

@@ -0,0 +1,25 @@
# Sealed-Mode CI Harness
This harness supports `DEVOPS-AIRGAP-57-002` by exercising services with the `sealed` flag, verifying that no outbound network traffic succeeds, and producing artefacts Authority can use for `AUTH-AIRGAP-57-001` gating.
## Workflow
1. Run `./run-sealed-ci.sh` from this directory (the script now boots the stack, applies the iptables guard, and captures artefacts automatically).
2. The harness:
- Launches `sealed-mode-compose.yml` with Authority/Signer/Attestor + Mongo.
- Snapshots iptables, injects a `STELLAOPS_SEALED` chain into `DOCKER-USER`/`OUTPUT`, and whitelists only loopback + RFC1918 ranges so container egress is denied.
- Repeatedly polls `/healthz` on `5088/6088/7088` to verify sealed-mode bindings stay healthy while egress is blocked.
- Executes `egress_probe.py`, which runs curl probes from inside the compose network to confirm off-cluster addresses are unreachable.
- Writes logs, iptables counters, and the summary contract to `artifacts/sealed-mode-ci/<timestamp>`.
3. `.gitea/workflows/build-test-deploy.yml` now includes a `sealed-mode-ci` job that runs this script on every push/PR and uploads the artefacts for `AUTH-AIRGAP-57-001`.
## Outputs
- `authority.health.log`, `signer.health.log`, `attestor.health.log`
- `iptables-docker-user.txt`, `iptables-output.txt`
- `egress-probe.json`
- `compose.log`, `compose.ps`
- `authority-sealed-ci.json` (single file Authority uses to validate the run)
## TODO
- [ ] Wire into offline kit smoke tests (DEVOPS-AIRGAP-58-001).
Refer to `docs/security/dpop-mtls-rollout.md` for cross-guild milestones.

View File

@@ -0,0 +1,8 @@
The command 'docker' could not be found in this WSL 2 distro.
We recommend to activate the WSL integration in Docker Desktop settings.
For details about using Docker Desktop with WSL 2, visit:
https://docs.docker.com/go/wsl2/

View File

@@ -0,0 +1,8 @@
The command 'docker' could not be found in this WSL 2 distro.
We recommend to activate the WSL integration in Docker Desktop settings.
For details about using Docker Desktop with WSL 2, visit:
https://docs.docker.com/go/wsl2/

View File

@@ -0,0 +1,54 @@
schemaVersion: 1
issuer: http://authority.sealed-ci.local
accessTokenLifetime: 00:02:00
refreshTokenLifetime: 01:00:00
identityTokenLifetime: 00:05:00
authorizationCodeLifetime: 00:05:00
deviceCodeLifetime: 00:15:00
pluginDirectories:
- /app
plugins:
configurationDirectory: /app/plugins
descriptors:
standard:
type: standard
assemblyName: StellaOps.Authority.Plugin.Standard
enabled: true
configFile: standard.yaml
storage:
connectionString: mongodb://sealedci:sealedci@mongo:27017/authority?authSource=admin
databaseName: authority
commandTimeout: 00:00:30
signing:
enabled: true
activeKeyId: sealed-ci
keyPath: /certificates/authority-signing-dev.pem
algorithm: ES256
keySource: file
bootstrap:
enabled: false
crypto:
providers: []
security:
senderConstraints:
dpop:
enabled: true
proofLifetime: 00:02:00
replayWindow: 00:05:00
nonce:
enabled: false
mtls:
enabled: false
airGap:
egress:
mode: Sealed
allowLoopback: true
allowPrivateNetworks: true
remediationDocumentationUrl: https://docs.stella-ops.org/airgap/sealed-ci
supportContact: airgap-ops@stella-ops.org
tenants:
- name: sealed-ci
roles:
operators:
scopes:
- policy:read

View File

@@ -0,0 +1,83 @@
#!/usr/bin/env python3
"""Run egress probes from the sealed-mode Docker network."""
from __future__ import annotations
import argparse
import json
import os
import shlex
import subprocess
import sys
import time
from datetime import datetime, timezone
from typing import List
DEFAULT_TARGETS = [
"https://example.com",
"https://www.cloudflare.com",
"https://releases.stella-ops.org/healthz",
]
def run_probe(image: str, network: str, target: str, timeout: int) -> dict:
cmd: List[str] = [
"docker",
"run",
"--rm",
"--network",
network,
image,
"-fsS",
"--max-time",
str(timeout),
target,
]
started = time.monotonic()
proc = subprocess.run(cmd, capture_output=True, text=True)
duration = time.monotonic() - started
status = "blocked" if proc.returncode != 0 else "connected"
return {
"target": target,
"status": status,
"durationSeconds": round(duration, 3),
"exitCode": proc.returncode,
"command": " ".join(shlex.quote(part) for part in cmd),
"stdout": proc.stdout.strip(),
"stderr": proc.stderr.strip(),
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--network", required=True, help="Docker network to join (compose project network)")
parser.add_argument("--image", default="curlimages/curl:8.6.0", help="Container image providing curl")
parser.add_argument("--timeout", type=int, default=10, help="Curl max-time for each probe (seconds)")
parser.add_argument("--output", required=True, help="Path to write JSON results")
parser.add_argument("targets", nargs="*", help="Override target URLs")
args = parser.parse_args()
targets = args.targets or DEFAULT_TARGETS
results = [run_probe(args.image, args.network, target, args.timeout) for target in targets]
passed = all(result["status"] == "blocked" for result in results)
payload = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"network": args.network,
"image": args.image,
"targets": results,
"passed": passed,
}
os.makedirs(os.path.dirname(args.output), exist_ok=True)
with open(args.output, "w", encoding="utf-8") as handle:
json.dump(payload, handle, ensure_ascii=False, indent=2)
handle.write("\n")
return 0 if passed else 1
if __name__ == "__main__":
try:
sys.exit(main())
except Exception as exc: # pragma: no cover
print(f"egress probe failed: {exc}", file=sys.stderr)
sys.exit(2)

View File

@@ -0,0 +1,18 @@
bootstrapUser:
username: sealed-admin
password: ChangeMe11!
passwordPolicy:
minimumLength: 8
requireUppercase: false
requireLowercase: true
requireDigit: true
requireSymbol: false
passwordHashing:
algorithm: Argon2id
memorySizeInKib: 8192
iterations: 2
parallelism: 1
lockout:
enabled: false
tokenSigning:
keyDirectory: /certificates

View File

@@ -0,0 +1,169 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
cd "$SCRIPT_DIR"
COMPOSE_FILE=${COMPOSE_FILE:-"$SCRIPT_DIR/sealed-mode-compose.yml"}
PROJECT_NAME=${COMPOSE_PROJECT_NAME:-sealedmode}
NETWORK_NAME="${PROJECT_NAME}_sealed-ci"
ARTIFACT_ROOT=${ARTIFACT_ROOT:-"$SCRIPT_DIR/artifacts/sealed-mode-ci"}
STAMP=$(date -u +"%Y%m%dT%H%M%SZ")
OUT_DIR="$ARTIFACT_ROOT/$STAMP"
mkdir -p "$OUT_DIR"
log() {
printf '[%s] %s\n' "$(date -u +%H:%M:%S)" "$*"
}
EXIT_CODE=0
IPTABLES_SNAPSHOT=""
cleanup() {
local exit_code=$?
log "Collecting docker compose logs"
docker compose -f "$COMPOSE_FILE" -p "$PROJECT_NAME" logs >"$OUT_DIR/compose.log" 2>&1 || true
docker compose -f "$COMPOSE_FILE" -p "$PROJECT_NAME" ps -a >"$OUT_DIR/compose.ps" 2>&1 || true
log "Tearing down sealed-mode stack"
docker compose -f "$COMPOSE_FILE" -p "$PROJECT_NAME" down -v >"$OUT_DIR/docker-down.log" 2>&1 || true
if [[ -n "$IPTABLES_SNAPSHOT" && -f "$IPTABLES_SNAPSHOT" ]]; then
log "Restoring iptables snapshot"
sudo iptables-restore <"$IPTABLES_SNAPSHOT" || true
rm -f "$IPTABLES_SNAPSHOT"
fi
log "Artifacts stored at $OUT_DIR"
exit $exit_code
}
trap cleanup EXIT
log "Pulling compose images (best effort)"
docker compose -f "$COMPOSE_FILE" -p "$PROJECT_NAME" pull --ignore-pull-failures || true
log "Starting sealed-mode stack"
docker compose -f "$COMPOSE_FILE" -p "$PROJECT_NAME" up -d --remove-orphans
wait_for_port() {
local port=$1
local label=$2
for attempt in $(seq 1 30); do
if curl -fsS --max-time 5 "http://127.0.0.1:${port}/healthz" >/dev/null 2>&1; then
log "$label responded on port $port"
return 0
fi
sleep 2
done
log "$label failed to respond on port $port"
return 1
}
wait_for_port 5088 "Authority" || EXIT_CODE=1
wait_for_port 6088 "Signer" || EXIT_CODE=1
wait_for_port 7088 "Attestor" || EXIT_CODE=1
log "Fetching probe helper image"
docker pull curlimages/curl:8.6.0 >/dev/null 2>&1 || true
log "Snapshotting iptables state"
IPTABLES_SNAPSHOT=$(mktemp)
sudo iptables-save >"$IPTABLES_SNAPSHOT"
log "Applying sealed-mode egress policy"
CHAIN="STELLAOPS_SEALED"
sudo iptables -N "$CHAIN" 2>/dev/null || sudo iptables -F "$CHAIN"
for cidr in 127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16; do
sudo iptables -A "$CHAIN" -d "$cidr" -j RETURN
done
sudo iptables -A "$CHAIN" -j LOG --log-prefix "stellaops-sealed-deny " --log-level 4
sudo iptables -A "$CHAIN" -j DROP
sudo iptables -I DOCKER-USER 1 -j "$CHAIN"
sudo iptables -I OUTPUT 1 -j "$CHAIN"
check_health() {
local name=$1
local port=$2
local url="http://127.0.0.1:${port}/healthz"
local log_file="$OUT_DIR/${name}.health.log"
local status="fail"
for attempt in $(seq 1 20); do
if curl -fsS --max-time 5 "$url" >"$log_file" 2>&1; then
status="pass"
break
fi
sleep 2
done
if [[ "$status" == "pass" ]]; then
log "$name health check succeeded"
else
log "$name health check failed"
EXIT_CODE=1
fi
local upper
upper=$(echo "$name" | tr '[:lower:]' '[:upper:]')
eval "${upper}_HEALTH_STATUS=$status"
eval "${upper}_HEALTH_URL=$url"
}
check_health authority 5088
check_health signer 6088
check_health attestor 7088
log "Running egress probe via docker network $NETWORK_NAME"
EGRESS_JSON="$OUT_DIR/egress-probe.json"
if python3 "$SCRIPT_DIR/egress_probe.py" --network "$NETWORK_NAME" --image curlimages/curl:8.6.0 --timeout 8 --output "$EGRESS_JSON"; then
EGRESS_STATUS="pass"
else
EGRESS_STATUS="fail"
EXIT_CODE=1
fi
log "Dumping iptables counters"
sudo iptables -v -x -L DOCKER-USER >"$OUT_DIR/iptables-docker-user.txt"
sudo iptables -v -x -L OUTPUT >"$OUT_DIR/iptables-output.txt"
log "Recording summary JSON"
export PROJECT_NAME NETWORK_NAME EGRESS_STATUS EGRESS_JSON
export AUTHORITY_HEALTH_STATUS SIGNER_HEALTH_STATUS ATTESTOR_HEALTH_STATUS
export AUTHORITY_HEALTH_URL SIGNER_HEALTH_URL ATTESTOR_HEALTH_URL
python3 - <<'PY' >"$OUT_DIR/authority-sealed-ci.json"
import json
import os
import sys
from datetime import datetime, timezone
summary = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"project": os.environ.get("PROJECT_NAME"),
"network": os.environ.get("NETWORK_NAME"),
"health": {
"authority": {
"status": os.environ.get("AUTHORITY_HEALTH_STATUS", "unknown"),
"url": os.environ.get("AUTHORITY_HEALTH_URL"),
"log": "authority.health.log",
},
"signer": {
"status": os.environ.get("SIGNER_HEALTH_STATUS", "unknown"),
"url": os.environ.get("SIGNER_HEALTH_URL"),
"log": "signer.health.log",
},
"attestor": {
"status": os.environ.get("ATTESTOR_HEALTH_STATUS", "unknown"),
"url": os.environ.get("ATTESTOR_HEALTH_URL"),
"log": "attestor.health.log",
},
},
"egressProbe": {
"status": os.environ.get("EGRESS_STATUS", "unknown"),
"report": os.path.basename(os.environ.get("EGRESS_JSON", "egress-probe.json")),
},
}
json.dump(summary, sys.stdout, indent=2)
print()
PY
if [[ $EXIT_CODE -eq 0 ]]; then
log "Sealed-mode CI run completed successfully"
else
log "Sealed-mode CI run completed with failures"
fi
exit $EXIT_CODE

View File

@@ -0,0 +1,83 @@
version: '3.9'
x-release-labels: &release-labels
com.stellaops.profile: 'sealed-ci'
com.stellaops.airgap.mode: 'sealed'
networks:
sealed-ci:
driver: bridge
volumes:
sealed-mongo-data:
services:
mongo:
image: docker.io/library/mongo@sha256:c258b26dbb7774f97f52aff52231ca5f228273a84329c5f5e451c3739457db49
command: ['mongod', '--bind_ip_all']
restart: unless-stopped
environment:
MONGO_INITDB_ROOT_USERNAME: sealedci
MONGO_INITDB_ROOT_PASSWORD: sealedci-secret
volumes:
- sealed-mongo-data:/data/db
networks:
- sealed-ci
labels: *release-labels
authority:
image: registry.stella-ops.org/stellaops/authority@sha256:a8e8faec44a579aa5714e58be835f25575710430b1ad2ccd1282a018cd9ffcdd
depends_on:
- mongo
restart: unless-stopped
environment:
ASPNETCORE_URLS: http://+:5088
STELLAOPS_AUTHORITY__ISSUER: http://authority.sealed-ci.local
STELLAOPS_AUTHORITY__MONGO__CONNECTIONSTRING: mongodb://sealedci:sealedci-secret@mongo:27017/authority?authSource=admin
STELLAOPS_AUTHORITY__PLUGINDIRECTORIES__0: /app/plugins
STELLAOPS_AUTHORITY__PLUGINS__CONFIGURATIONDIRECTORY: /app/plugins
STELLAOPS_AUTHORITY__SECURITY__SENDERCONSTRAINTS__DPOP__ENABLED: 'true'
STELLAOPS_AUTHORITY__SECURITY__SENDERCONSTRAINTS__MTLS__ENABLED: 'true'
STELLAOPS_AUTHORITY__AIRGAP__EGRESS__MODE: Sealed
volumes:
- ./authority.harness.yaml:/etc/authority.yaml:ro
- ./plugins:/app/plugins:ro
- ../../../certificates:/certificates:ro
ports:
- '5088:5088'
networks:
- sealed-ci
labels: *release-labels
signer:
image: registry.stella-ops.org/stellaops/signer@sha256:8bfef9a75783883d49fc18e3566553934e970b00ee090abee9cb110d2d5c3298
depends_on:
- authority
restart: unless-stopped
environment:
ASPNETCORE_URLS: http://+:6088
SIGNER__AUTHORITY__BASEURL: http://authority:5088
SIGNER__POE__INTROSPECTURL: http://authority:5088/device-code
SIGNER__STORAGE__MONGO__CONNECTIONSTRING: mongodb://sealedci:sealedci-secret@mongo:27017/signer?authSource=admin
SIGNER__SEALED__MODE: Enabled
ports:
- '6088:6088'
networks:
- sealed-ci
labels: *release-labels
attestor:
image: registry.stella-ops.org/stellaops/attestor@sha256:5cc417948c029da01dccf36e4645d961a3f6d8de7e62fe98d845f07cd2282114
depends_on:
- signer
restart: unless-stopped
environment:
ASPNETCORE_URLS: http://+:7088
ATTESTOR__SIGNER__BASEURL: http://signer:6088
ATTESTOR__MONGO__CONNECTIONSTRING: mongodb://sealedci:sealedci-secret@mongo:27017/attestor?authSource=admin
ATTESTOR__SEALED__MODE: Enabled
ports:
- '7088:7088'
networks:
- sealed-ci
labels: *release-labels