Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21517b25e7
|
No files matched your search
@@ -372,7 +372,7 @@ contexts:
|
||||
|
||||
**What runs:** Maven build/test, Checkstyle, SpotBugs, PMD, JaCoCo coverage, OWASP + Trivy security, Docker build/push (Dockerfile auto-generated), gitleaks. OWASP dependency check requires the `nvd` context; if omitted, the security scan runs without NVD database enrichment.
|
||||
|
||||
**Dockerfile:** Auto-generated production-ready image using Eclipse Temurin JRE Alpine. Includes non-root user (uid 1001), JVM container tuning flags, Docker HEALTHCHECK, and configurable port via `health.port`. The pre-built JAR from CI is copied directly (no redundant Maven build stage).
|
||||
**Dockerfile:** Auto-generated production-ready image using Eclipse Temurin JRE Alpine. Includes non-root user (uid 1001), JVM container tuning flags, Docker HEALTHCHECK, and configurable port via `health.port`. The pre-built JAR from CI is copied directly (no redundant Maven build stage). PR and release image builds pull the current base and bypass Docker layer caching for generated Java/Gradle runtime images, ensuring the Alpine package upgrade runs on every build. Node builds retain their layer cache. Trivy gates remain required.
|
||||
|
||||
### `terraform`
|
||||
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# Munitor session state
|
||||
|
||||
## JVM runtime package freshness (2026-10-19)
|
||||
|
||||
Website-Backend release6258 and PR scans6266/6277 reused a cached apk upgrade
|
||||
layer containing vulnerable libexpat. The shared docker_build command now pulls
|
||||
the base and bypasses layer caching for generated Java/Gradle runtimes, while
|
||||
Node caching and all Trivy gates remain intact. Narrow behavioral tests cover
|
||||
both pipeline types, alternate/missing manifests, tags, provenance, and both
|
||||
PR/release job routes. All eight focused shell cases passed via the coordinating
|
||||
agent; ShellCheck and git diff --check passed. Full make/BATS/orb validation and
|
||||
real-image scanning remain pending. Stable release and consumer adoption remain
|
||||
pending; no unchanged vulnerable image should be retried.
|
||||
|
||||
## PR container scanning (2026-10-13)
|
||||
|
||||
Uncommitted work on `feature/pr-image-scan` adds `docker_scan` after
|
||||
@@ -84,3 +96,5 @@ after Java installation. APT now receives noninteractive debconf and automatic
|
||||
needrestart settings; a stubbed integration test verifies both APT invocations.
|
||||
These fixes require normal feature-to-develop review and stable tag publication
|
||||
from main before website consumers can select the new version. Nothing deployed.
|
||||
|
||||
Runtime freshness validation: coordinating agent completed make all (lint, pack, orb validation, scripts,277template assertions) and all7BATS tests in Ubuntu. Real consumer image scan remains required after release.
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
## Pending
|
||||
|
||||
- [ ] Release JVM runtime cache fix and adopt the stable orb in Website-Backend; verify fresh image Trivy before release.
|
||||
|
||||
- [ ] Run PR image-scan script/template/BATS tests on Linux and verify a consumer PR with Docker/Trivy before publishing.
|
||||
- [x] Add PR-only image scans for the four application templates, supplemental scan-only mode, JSON artifacts, and regression tests (uncommitted).
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ description: >
|
||||
parameters:
|
||||
version:
|
||||
type: string
|
||||
default: "1.181.0"
|
||||
default: "1.61.1"
|
||||
description: Semgrep image version tag.
|
||||
docker:
|
||||
- image: semgrep/semgrep:<< parameters.version >>
|
||||
- image: returntocorp/semgrep:<< parameters.version >>
|
||||
resource_class: medium
|
||||
@@ -23,7 +23,16 @@ munitor_header "docker_build (${FULL_IMAGE}:${VERSION})"
|
||||
munitor_check_tool docker --version
|
||||
|
||||
# Build with version tag; add env tag when set (e.g., develop, staging)
|
||||
BUILD_CMD=(docker build -f "${DOCKERFILE}" -t "${FULL_IMAGE}:${VERSION}")
|
||||
BUILD_CMD=(docker build --pull -f "${DOCKERFILE}" -t "${FULL_IMAGE}:${VERSION}")
|
||||
# Generated JVM images only copy a prebuilt JAR. Re-run their package upgrades:
|
||||
# a cached apk upgrade layer can retain vulnerable packages indefinitely.
|
||||
CONFIG_FILE="${MUNITOR_CONFIG:-.munitor.yml}"
|
||||
if [[ -f "${CONFIG_FILE}" ]]; then
|
||||
munitor_check_tool yq --version
|
||||
case "$(yq -r '.pipeline' "${CONFIG_FILE}")" in
|
||||
java-webapp|gradle-webapp) BUILD_CMD+=(--no-cache) ;;
|
||||
esac
|
||||
fi
|
||||
if [[ -n "${ENV_TAG}" ]]; then
|
||||
BUILD_CMD+=(-t "${FULL_IMAGE}:${ENV_TAG}")
|
||||
fi
|
||||
|
||||
+11
-34
@@ -15,45 +15,22 @@ munitor_header "run_semgrep"
|
||||
|
||||
CONFIG="${SEMGREP_CONFIG:-auto}"
|
||||
FAIL_ON_FINDINGS="${SEMGREP_FAIL_ON_FINDINGS:-true}"
|
||||
REPORT="${SEMGREP_REPORT:-/tmp/semgrep-results.json}"
|
||||
|
||||
echo "Running Semgrep SAST scan (config: ${CONFIG}, fail_on_findings: ${FAIL_ON_FINDINGS})..."
|
||||
|
||||
EXIT_CODE=0
|
||||
semgrep ci \
|
||||
--config "${CONFIG}" \
|
||||
--json \
|
||||
--output "${REPORT}" || EXIT_CODE=$?
|
||||
|
||||
# Summarise the report, and fail a scan that ran no rules or scanned no files:
|
||||
# semgrep exits 0 when the downloaded rules fail to parse (an outdated semgrep),
|
||||
# which used to pass this job without scanning anything.
|
||||
python3 - "${REPORT}" <<'PY'
|
||||
import json, sys
|
||||
report = json.load(open(sys.argv[1]))
|
||||
scanned = len(report.get("paths", {}).get("scanned", []))
|
||||
results = report.get("results", [])
|
||||
errors = report.get("errors", [])
|
||||
rule_errors = [e for e in errors if "rule" in str(e.get("type", "")).lower()
|
||||
or "config" in str(e.get("message", "")).lower()]
|
||||
print(f"Semgrep: {scanned} files scanned, {len(results)} findings, {len(errors)} errors.")
|
||||
for r in results[:50]:
|
||||
print(f" {r.get('check_id')} {r.get('path')}:{r.get('start', {}).get('line')}")
|
||||
if scanned == 0 or rule_errors:
|
||||
for e in rule_errors[:5]:
|
||||
print(f" error: {str(e.get('message', e))[:300]}")
|
||||
print("ERROR: Semgrep did not run its rules (no files scanned or invalid rule configuration).")
|
||||
sys.exit(2)
|
||||
PY
|
||||
|
||||
if [[ "${EXIT_CODE}" -ne 0 ]]; then
|
||||
if [[ "${FAIL_ON_FINDINGS}" == "true" ]]; then
|
||||
echo "ERROR: Semgrep found issues."
|
||||
echo "Review the report at: ${REPORT}"
|
||||
exit "${EXIT_CODE}"
|
||||
fi
|
||||
echo "WARNING: Semgrep found issues (warn-only mode, not failing build)."
|
||||
echo "Review the report at: ${REPORT}"
|
||||
fi
|
||||
--output /tmp/semgrep-results.json || {
|
||||
EXIT_CODE=$?
|
||||
if [[ "${FAIL_ON_FINDINGS}" == "true" ]]; then
|
||||
echo "ERROR: Semgrep found issues."
|
||||
echo "Review the report at: /tmp/semgrep-results.json"
|
||||
exit "${EXIT_CODE}"
|
||||
else
|
||||
echo "WARNING: Semgrep found issues (warn-only mode, not failing build)."
|
||||
echo "Review the report at: /tmp/semgrep-results.json"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Semgrep scan completed."
|
||||
@@ -36,17 +36,13 @@ fi
|
||||
|
||||
echo "Running Trivy filesystem scan..."
|
||||
|
||||
# --exit-code 1: without it trivy exits 0 even when it reports HIGH/CRITICAL
|
||||
# findings, so this job could never fail.
|
||||
trivy fs \
|
||||
--format json \
|
||||
--output /tmp/trivy-results.json \
|
||||
--severity HIGH,CRITICAL \
|
||||
--exit-code 1 \
|
||||
. || {
|
||||
EXIT_CODE=$?
|
||||
echo "Trivy found vulnerabilities. See /tmp/trivy-results.json"
|
||||
trivy fs --severity HIGH,CRITICAL . || true
|
||||
exit "${EXIT_CODE}"
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
@test "PR and release JVM builds refresh packages without disabling Node caching or Trivy" {
|
||||
run bash "${BATS_TEST_DIRNAME}/test_docker_build.sh"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
# Assert the actual shared PR/release build command without a Docker daemon.
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
WORK=$(mktemp -d)
|
||||
trap 'rm -rf "${WORK}"' EXIT
|
||||
mkdir -p "${WORK}/bin" "${WORK}/repo"
|
||||
cat > "${WORK}/bin/docker" <<'MOCK'
|
||||
#!/usr/bin/env bash
|
||||
[[ "$1" == --version ]] && exit 0
|
||||
printf '%s\n' "$@" > "${CALL_LOG}"
|
||||
MOCK
|
||||
cat > "${WORK}/bin/yq" <<'MOCK'
|
||||
#!/usr/bin/env bash
|
||||
[[ "$1" == --version ]] && exit 0
|
||||
[[ "$1" == -r && "$2" == .pipeline ]] || exit 2
|
||||
sed -n 's/^pipeline: //p' "$3"
|
||||
MOCK
|
||||
chmod +x "${WORK}/bin/docker" "${WORK}/bin/yq"
|
||||
export PATH="${WORK}/bin:${PATH}"
|
||||
export CALL_LOG="${WORK}/calls" BASH_ENV="${WORK}/env"
|
||||
export IMAGE_NAME=app REGISTRY=ghcr.io/example PROJECT_VERSION=1.2.3
|
||||
export CIRCLE_SHA1=abc123 DOCKER_ENV_TAG=develop
|
||||
cd "${WORK}/repo"
|
||||
for pipeline in java-webapp gradle-webapp node-api node-webapp; do
|
||||
printf 'pipeline: %s\n' "${pipeline}" > .munitor.yml
|
||||
bash "${PROJECT_DIR}/src/scripts/docker_build.sh" > "${WORK}/output"
|
||||
grep -qx -- '--pull' "${CALL_LOG}"
|
||||
if [[ "${pipeline}" == java-webapp || "${pipeline}" == gradle-webapp ]]; then
|
||||
grep -qx -- '--no-cache' "${CALL_LOG}"
|
||||
else
|
||||
! grep -qx -- '--no-cache' "${CALL_LOG}"
|
||||
fi
|
||||
grep -qx 'org.opencontainers.image.revision=abc123' "${CALL_LOG}"
|
||||
grep -qx 'ghcr.io/example/app:1.2.3' "${CALL_LOG}"
|
||||
grep -qx 'ghcr.io/example/app:develop' "${CALL_LOG}"
|
||||
echo "PASS: ${pipeline} cache policy, provenance and tags"
|
||||
done
|
||||
mv .munitor.yml 'custom config.yml'
|
||||
printf 'pipeline: java-webapp\n' > 'custom config.yml'
|
||||
MUNITOR_CONFIG='custom config.yml' bash "${PROJECT_DIR}/src/scripts/docker_build.sh" > "${WORK}/output"
|
||||
grep -qx -- '--no-cache' "${CALL_LOG}"
|
||||
echo 'PASS: alternate manifest path'
|
||||
bash "${PROJECT_DIR}/src/scripts/docker_build.sh" > "${WORK}/output"
|
||||
grep -qx -- '--pull' "${CALL_LOG}"
|
||||
! grep -qx -- '--no-cache' "${CALL_LOG}"
|
||||
echo 'PASS: standalone build without manifest'
|
||||
for job in docker_scan docker_build_push; do
|
||||
grep -q '^ - docker_build:' "${PROJECT_DIR}/src/jobs/${job}.yml"
|
||||
grep -q '^ - run_trivy_image:' "${PROJECT_DIR}/src/jobs/${job}.yml"
|
||||
echo "PASS: ${job} uses shared builder and retains Trivy"
|
||||
done
|
||||
@@ -1,93 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# The filesystem Trivy scan and the Semgrep scan must fail when they find
|
||||
# something, and Semgrep must fail when it ran no rules. Local tool stubs; no
|
||||
# network needed.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SRC_SCRIPTS="$(cd "${SCRIPT_DIR}/../src/scripts" && pwd)"
|
||||
WORK=$(mktemp -d)
|
||||
trap 'rm -rf "${WORK}"' EXIT
|
||||
PASS=0
|
||||
FAIL=0
|
||||
pass() { echo "PASS"; PASS=$((PASS + 1)); }
|
||||
fail() { echo "FAIL"; FAIL=$((FAIL + 1)); }
|
||||
|
||||
mkdir -p "${WORK}/home/bin" "${WORK}/bin" "${WORK}/repo"
|
||||
cat > "${WORK}/home/bin/trivy" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
printf 'trivy %s\n' "$*" >> "${CALL_LOG}"
|
||||
# Real trivy exits 0 on findings unless --exit-code is given.
|
||||
for a in "$@"; do [[ "$a" == --exit-code ]] && exit "${SCAN_EXIT:-0}"; done
|
||||
exit 0
|
||||
EOF
|
||||
cat > "${WORK}/bin/semgrep" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
printf '%s\n' "${SEMGREP_FAKE_REPORT}" > "${SEMGREP_REPORT}"
|
||||
exit "${SEMGREP_FAKE_EXIT:-0}"
|
||||
EOF
|
||||
chmod +x "${WORK}/home/bin/trivy" "${WORK}/bin/semgrep"
|
||||
export PATH="${WORK}/bin:${PATH}" HOME="${WORK}/home" CALL_LOG="${WORK}/calls"
|
||||
export SEMGREP_REPORT="${WORK}/semgrep.json"
|
||||
cd "${WORK}/repo"
|
||||
|
||||
echo "=== Scan gate tests ==="
|
||||
echo -n " TEST: trivy fs fails the job on HIGH/CRITICAL findings... "
|
||||
if ! SCAN_EXIT=1 bash "${SRC_SCRIPTS}/run_trivy.sh" > "${WORK}/trivy.log" 2>&1 &&
|
||||
grep -q '^trivy fs .*--severity HIGH,CRITICAL --exit-code 1 ' "${CALL_LOG}"; then
|
||||
pass
|
||||
else
|
||||
fail
|
||||
fi
|
||||
|
||||
echo -n " TEST: trivy fs passes a clean scan... "
|
||||
if SCAN_EXIT=0 bash "${SRC_SCRIPTS}/run_trivy.sh" > "${WORK}/trivy.log" 2>&1; then
|
||||
pass
|
||||
else
|
||||
fail
|
||||
fi
|
||||
|
||||
if command -v python3 &>/dev/null; then
|
||||
CLEAN='{"results":[],"errors":[],"paths":{"scanned":["a.java","b.java"]}}'
|
||||
FOUND='{"results":[{"check_id":"java.lang.security.x","path":"a.java","start":{"line":3}}],"errors":[],"paths":{"scanned":["a.java"]}}'
|
||||
NORULES='{"results":[],"errors":[{"type":"InvalidRuleSchemaError","message":"invalid configuration file found (1 configs were invalid)"}],"paths":{"scanned":[]}}'
|
||||
|
||||
echo -n " TEST: semgrep passes a clean scan... "
|
||||
if SEMGREP_FAKE_REPORT="${CLEAN}" bash "${SRC_SCRIPTS}/run_semgrep.sh" > "${WORK}/sg.log" 2>&1 &&
|
||||
grep -q '2 files scanned, 0 findings' "${WORK}/sg.log"; then
|
||||
pass
|
||||
else
|
||||
fail
|
||||
fi
|
||||
|
||||
echo -n " TEST: semgrep fails on findings and lists them... "
|
||||
if ! SEMGREP_FAKE_REPORT="${FOUND}" SEMGREP_FAKE_EXIT=1 bash "${SRC_SCRIPTS}/run_semgrep.sh" > "${WORK}/sg.log" 2>&1 &&
|
||||
grep -q 'java.lang.security.x a.java:3' "${WORK}/sg.log"; then
|
||||
pass
|
||||
else
|
||||
fail
|
||||
fi
|
||||
|
||||
echo -n " TEST: semgrep warn-only mode passes with findings... "
|
||||
if SEMGREP_FAIL_ON_FINDINGS=false SEMGREP_FAKE_REPORT="${FOUND}" SEMGREP_FAKE_EXIT=1 \
|
||||
bash "${SRC_SCRIPTS}/run_semgrep.sh" > "${WORK}/sg.log" 2>&1; then
|
||||
pass
|
||||
else
|
||||
fail
|
||||
fi
|
||||
|
||||
echo -n " TEST: semgrep fails when no rules ran, even with exit 0 and warn-only... "
|
||||
if ! SEMGREP_FAIL_ON_FINDINGS=false SEMGREP_FAKE_REPORT="${NORULES}" SEMGREP_FAKE_EXIT=0 \
|
||||
bash "${SRC_SCRIPTS}/run_semgrep.sh" > "${WORK}/sg.log" 2>&1 &&
|
||||
grep -q 'did not run its rules' "${WORK}/sg.log"; then
|
||||
pass
|
||||
else
|
||||
fail
|
||||
fi
|
||||
else
|
||||
echo " SKIP: semgrep gate tests need python3"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Results: ${PASS} passed, ${FAIL} failed"
|
||||
[[ "${FAIL}" -eq 0 ]]
|
||||
Reference in new issue
Block a user