titan-iac/services/quality/zap-baseline-configmap.yaml
jenkins 0da9e4c82d refactor: restructure services layout, retire oceanus, add aether scaffolding
- Move flat service manifests into structured subdirs (apps/, bootstrap-jobs/,
  repair-jobs/, migration-jobs/, validation-jobs/, node-ops/, networking/)
- Retire oneoffs/ directories across services
- Remove oceanus cluster and its host roles; add aether cluster + terraform scaffolding
- Reorganize scripts/ into ops/, render/, sync/, manual-tests/
- Add Makefile with render/validate/test/flux targets and repo-structure tests
- Update flux-system application CRs to the new paths
- Add hermes-automated-triage-24h-plan knowledge doc (+ comms mirror)
- Refresh knowledge catalogs, dashboards, vmalert rules, quality contract

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 16:21:36 -03:00

181 lines
6.3 KiB
YAML

# services/quality/zap-baseline-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: zap-baseline-config
namespace: quality
data:
targets.txt: |
https://bstein.dev
https://chat.ai.bstein.dev
https://sso.bstein.dev
https://auth.bstein.dev
https://scm.bstein.dev
https://ci.bstein.dev
https://registry.bstein.dev
https://quality.bstein.dev
https://secret.bstein.dev
https://vault.bstein.dev
https://logs.bstein.dev
https://metrics.bstein.dev
https://stream.bstein.dev
https://pegasus.bstein.dev
https://tasks.bstein.dev
https://cloud.bstein.dev
https://notes.bstein.dev
https://budget.bstein.dev
https://money.bstein.dev
https://health.bstein.dev
https://agent.bstein.dev
https://cassandra.bstein.dev
https://veles.bstein.dev
https://matrix.live.bstein.dev
https://live.bstein.dev
https://wolf.bstein.dev
https://mail.bstein.dev
https://recovery.bstein.dev
https://backup.bstein.dev
run_zap_baseline.sh: |
#!/usr/bin/env bash
set -euo pipefail
target_file="${ZAP_TARGET_FILE:-/zap/config/targets.txt}"
pushgateway="${PUSHGATEWAY_URL:-http://platform-quality-gateway.monitoring.svc.cluster.local:9091}"
job_name="${QUALITY_GATE_JOB_NAME:-platform-security-zap}"
spider_minutes="${ZAP_SPIDER_MINUTES:-1}"
max_scan_minutes="${ZAP_MAX_SCAN_MINUTES:-5}"
work_dir="${ZAP_WORK_DIR:-/zap/wrk}"
metrics_path="${work_dir}/zap-baseline.prom"
reports_dir="${work_dir}/reports"
mkdir -p "${reports_dir}"
: > "${metrics_path}"
python3 - <<'PY' > "${metrics_path}"
print("# TYPE platform_zap_baseline_target_up gauge")
print("# TYPE platform_zap_baseline_scan_status gauge")
print("# TYPE platform_zap_baseline_alerts_total gauge")
print("# TYPE platform_zap_baseline_target_health_percent gauge")
print("# TYPE platform_zap_baseline_last_run_timestamp_seconds gauge")
PY
while IFS= read -r raw_target || [ -n "${raw_target}" ]; do
target="$(printf '%s' "${raw_target}" | sed 's/#.*//' | xargs)"
[ -n "${target}" ] || continue
host="$(python3 - "${target}" <<'PY'
import sys
from urllib.parse import urlparse
print(urlparse(sys.argv[1]).hostname or "unknown")
PY
)"
safe_host="$(printf '%s' "${host}" | tr -c 'A-Za-z0-9_.-' '_')"
json_report="${reports_dir}/${safe_host}.json"
html_report="${reports_dir}/${safe_host}.html"
markdown_report="${reports_dir}/${safe_host}.md"
xml_report="${reports_dir}/${safe_host}.xml"
set +e
zap-baseline.py \
-t "${target}" \
-m "${spider_minutes}" \
-T "${max_scan_minutes}" \
-I \
-J "${json_report}" \
-r "${html_report}" \
-w "${markdown_report}" \
-x "${xml_report}"
zap_rc=$?
set -e
python3 - "${target}" "${host}" "${json_report}" "${zap_rc}" >> "${metrics_path}" <<'PY'
import json
import sys
import time
from pathlib import Path
target, host, report_path, zap_rc_raw = sys.argv[1:5]
try:
zap_rc = int(zap_rc_raw)
except ValueError:
zap_rc = 3
def esc(value: str) -> str:
return value.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"')
def labels(extra: dict[str, str]) -> str:
base = {"target": target, "host": host}
base.update(extra)
return "{" + ",".join(f'{key}="{esc(value)}"' for key, value in base.items()) + "}"
risk_counts = {"high": 0, "medium": 0, "low": 0, "informational": 0, "unknown": 0}
completed = Path(report_path).exists()
if completed:
try:
payload = json.loads(Path(report_path).read_text(encoding="utf-8"))
except json.JSONDecodeError:
completed = False
payload = {}
for site in payload.get("site", []) if isinstance(payload, dict) else []:
if not isinstance(site, dict):
continue
alerts = site.get("alerts", [])
if not isinstance(alerts, list):
continue
for alert in alerts:
if not isinstance(alert, dict):
continue
risk_code = str(alert.get("riskcode") or "").strip()
risk_name = str(alert.get("risk") or alert.get("riskdesc") or "unknown").split()[0].lower()
risk = {
"3": "high",
"2": "medium",
"1": "low",
"0": "informational",
}.get(risk_code, risk_name)
if risk not in risk_counts:
risk = "unknown"
instances = alert.get("instances")
count = len(instances) if isinstance(instances, list) and instances else 1
risk_counts[risk] += count
if not completed or zap_rc not in {0, 1, 2}:
status = "error"
elif risk_counts["high"] > 0:
status = "fail"
elif risk_counts["medium"] > 0 or risk_counts["low"] > 0:
status = "warn"
else:
status = "ok"
health = 0 if status in {"error", "fail"} else 85 if status == "warn" else 100
print(f"platform_zap_baseline_target_up{labels({})} {1 if completed else 0}")
for candidate in ["ok", "warn", "fail", "error"]:
print(f'platform_zap_baseline_scan_status{labels({"status": candidate})} {1 if status == candidate else 0}')
for risk, count in risk_counts.items():
print(f'platform_zap_baseline_alerts_total{labels({"risk": risk})} {count}')
print(f"platform_zap_baseline_target_health_percent{labels({})} {health}")
print(f"platform_zap_baseline_last_run_timestamp_seconds{labels({})} {int(time.time())}")
PY
done < "${target_file}"
python3 - "${pushgateway}" "${job_name}" "${metrics_path}" <<'PY'
import sys
import urllib.request
from pathlib import Path
pushgateway, job_name, metrics_path = sys.argv[1:4]
payload = Path(metrics_path).read_bytes()
url = f"{pushgateway.rstrip('/')}/metrics/job/{job_name}"
request = urllib.request.Request(
url,
data=payload,
method="PUT",
headers={"Content-Type": "text/plain"},
)
with urllib.request.urlopen(request, timeout=15) as response:
if response.status >= 400:
raise SystemExit(f"pushgateway returned HTTP {response.status}")
PY