Fix Bnd 7 test import #22
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Security release gate | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| push: | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: distributed-cache-security-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| test-build-scan: | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 150 | |
| env: | |
| TRIVY_IMAGE: aquasec/trivy:0.74.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969 | |
| steps: | |
| - name: Check out candidate | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Install verified Temurin LTS JDK | |
| shell: bash | |
| env: | |
| JDK_URL: https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.4.1%2B1/OpenJDK25U-jdk_x64_linux_hotspot_25.0.4.1_1.tar.gz | |
| JDK_SHA256: dbb698396d478e7fa2b1e50f4103324b2a99b90569ee27c33f2261f9215cf41e | |
| run: | | |
| set -euo pipefail | |
| archive="$RUNNER_TEMP/temurin-jdk.tar.gz" | |
| java_home="$RUNNER_TEMP/temurin-jdk" | |
| curl --proto '=https' --tlsv1.2 --fail --silent --show-error --location \ | |
| --output "$archive" "$JDK_URL" | |
| printf '%s %s\n' "$JDK_SHA256" "$archive" | sha256sum -c - | |
| mkdir -p "$java_home" | |
| tar -xzf "$archive" -C "$java_home" --strip-components=1 | |
| printf 'JAVA_HOME=%s\n' "$java_home" >> "$GITHUB_ENV" | |
| printf '%s/bin\n' "$java_home" >> "$GITHUB_PATH" | |
| "$java_home/bin/java" -version | |
| - name: Install pinned evidence verifier runtime | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 | |
| with: | |
| python-version: '3.14.7' | |
| check-latest: false | |
| update-environment: true | |
| - name: Verify candidate identity and supply-chain pins | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| test -z "$(git status --porcelain)" | |
| test "$(git rev-parse 60c31e3750cbad64f5720e2e02f0a9830973193c^{commit})" = \ | |
| 60c31e3750cbad64f5720e2e02f0a9830973193c | |
| git merge-base --is-ancestor 60c31e3750cbad64f5720e2e02f0a9830973193c HEAD | |
| grep -Fxq 'distributionSha256Sum=55fadd669532a3205d5db95f490bf13971d8b0843526f407f29db0e61f074ab3' \ | |
| .mvn/wrapper/maven-wrapper.properties | |
| test "$(python --version)" = 'Python 3.14.7' | |
| mkdir -p evidence | |
| git rev-parse HEAD > evidence/source-revision.txt | |
| sha256sum .mvn/wrapper/maven-wrapper.properties > evidence/source-locks.sha256 | |
| git ls-files -- 'pom.xml' '**/pom.xml' '.mvn/**' 'mvnw' 'mvnw.cmd' \ | |
| | LC_ALL=C sort -u > evidence/maven-input-files.txt | |
| while IFS= read -r source_path; do | |
| sha256sum -- "$source_path" | |
| done < evidence/maven-input-files.txt > evidence/maven-inputs.sha256 | |
| - name: Run focused legitimate and malicious regression suite | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| core_tests='com.hazelcast.aws.AwsMetadataApiTest,com.hazelcast.azure.TagTest,com.hazelcast.config.XmlSchemaSourceSecurityTest,com.hazelcast.console.ConsoleAppTest,com.hazelcast.core.server.HazelcastMemberStarterTest,com.hazelcast.dataconnection.impl.JdbcDataConnectionTest,com.hazelcast.dataconnection.impl.JdbcUrlPolicyTest,com.hazelcast.dataconnection.impl.hazelcastdataconnection.HazelcastDataConnectionConfigLoaderTest,com.hazelcast.dataconnection.impl.jdbcproperties.HikariTranslatorTest,com.hazelcast.flakeidgen.impl.FlakeIdGeneratorProxyTest,com.hazelcast.gcp.LabelTest,com.hazelcast.gcp.UtilsTest,com.hazelcast.internal.config.ConfigLoaderSecurityTest,com.hazelcast.internal.diagnostics.DiagnosticsLogFileTest,com.hazelcast.internal.serialization.impl.ByteArrayObjectDataIntegrationTest,com.hazelcast.internal.serialization.impl.ObjectDataInputStreamIntegrationTest,com.hazelcast.internal.serialization.impl.UnsafeObjectDataInputIntegrationTest,com.hazelcast.internal.util.ClockTest,com.hazelcast.internal.util.HashUtilTest,com.hazelcast.internal.util.SecureFileAccessTest,com.hazelcast.internal.util.collection.LongHashSetTest,com.hazelcast.internal.util.concurrent.BackoffIdleStrategyTest,com.hazelcast.internal.util.phonehome.PhoneHomeDifferentConfigTest,com.hazelcast.internal.util.phonehome.PhoneHomeIntegrationTest,com.hazelcast.internal.util.XmlUtilTest,com.hazelcast.jet.impl.submitjob.memberside.JobUploadStatusTest,com.hazelcast.jet.impl.submitjob.memberside.validator.JarOnClientValidatorTest,com.hazelcast.jet.retry.impl.IntervalFunctionTest,com.hazelcast.kubernetes.KubernetesApiOriginPolicyTest,com.hazelcast.kubernetes.KubernetesConfigTest,com.hazelcast.spi.discovery.multicast.MulticastDiscoveryStrategyTest,com.hazelcast.spi.utils.RestClientTest,com.hazelcast.spi.utils.RetryUtilsTest' | |
| all_tests="$core_tests,com.hazelcast.jet.sql.impl.parse.QueryParserTest,com.hazelcast.buildutils.ExportPackageViewerTest,com.hazelcast.jet.cdc.OperationTest" | |
| timeout --signal=TERM --kill-after=30s 30m ./mvnw -B \ | |
| -pl hazelcast,hazelcast-sql,hazelcast-build-utils,extensions/cdc-debezium,extensions/mongodb \ | |
| -am \ | |
| -Dcheckstyle.skip=true \ | |
| -Dsurefire.failIfNoSpecifiedTests=false \ | |
| -Dtest="$all_tests" \ | |
| test | |
| timeout --signal=TERM --kill-after=30s 10m ./mvnw -B \ | |
| -pl hazelcast -am \ | |
| -Dcheckstyle.skip=true \ | |
| -Dsurefire.failIfNoSpecifiedTests=false \ | |
| -Dtest=com.hazelcast.query.impl.TypeConverterTest \ | |
| test | |
| timeout --signal=TERM --kill-after=30s 15m ./mvnw -B \ | |
| -pl hazelcast -am \ | |
| -Dcheckstyle.skip=true \ | |
| -Dsurefire.failIfNoSpecifiedTests=false \ | |
| -Dtest=com.hazelcast.jet.impl.deployment.ProcessorClassLoaderTest \ | |
| test | |
| scripts/check-xml-parser-hardening | |
| scripts/check-numeric-conversion-hardening | |
| - name: Verify every required test suite was discovered | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| python3 - <<'PY' | |
| import pathlib | |
| import xml.etree.ElementTree as ET | |
| core = ''' | |
| com.hazelcast.aws.AwsMetadataApiTest | |
| com.hazelcast.azure.TagTest | |
| com.hazelcast.config.XmlSchemaSourceSecurityTest | |
| com.hazelcast.console.ConsoleAppTest | |
| com.hazelcast.core.server.HazelcastMemberStarterTest | |
| com.hazelcast.dataconnection.impl.JdbcDataConnectionTest | |
| com.hazelcast.dataconnection.impl.JdbcUrlPolicyTest | |
| com.hazelcast.dataconnection.impl.hazelcastdataconnection.HazelcastDataConnectionConfigLoaderTest | |
| com.hazelcast.dataconnection.impl.jdbcproperties.HikariTranslatorTest | |
| com.hazelcast.flakeidgen.impl.FlakeIdGeneratorProxyTest | |
| com.hazelcast.gcp.LabelTest | |
| com.hazelcast.gcp.UtilsTest | |
| com.hazelcast.internal.config.ConfigLoaderSecurityTest | |
| com.hazelcast.internal.diagnostics.DiagnosticsLogFileTest | |
| com.hazelcast.internal.serialization.impl.ByteArrayObjectDataIntegrationTest | |
| com.hazelcast.internal.serialization.impl.ObjectDataInputStreamIntegrationTest | |
| com.hazelcast.internal.serialization.impl.UnsafeObjectDataInputIntegrationTest | |
| com.hazelcast.internal.util.ClockTest | |
| com.hazelcast.internal.util.HashUtilTest | |
| com.hazelcast.internal.util.SecureFileAccessTest | |
| com.hazelcast.internal.util.collection.LongHashSetTest | |
| com.hazelcast.internal.util.concurrent.BackoffIdleStrategyTest | |
| com.hazelcast.internal.util.phonehome.PhoneHomeDifferentConfigTest | |
| com.hazelcast.internal.util.phonehome.PhoneHomeIntegrationTest | |
| com.hazelcast.internal.util.XmlUtilTest | |
| com.hazelcast.jet.impl.deployment.ProcessorClassLoaderTest | |
| com.hazelcast.jet.impl.submitjob.memberside.JobUploadStatusTest | |
| com.hazelcast.jet.impl.submitjob.memberside.validator.JarOnClientValidatorTest | |
| com.hazelcast.jet.retry.impl.IntervalFunctionTest | |
| com.hazelcast.kubernetes.KubernetesApiOriginPolicyTest | |
| com.hazelcast.kubernetes.KubernetesConfigTest | |
| com.hazelcast.query.impl.TypeConverterTest | |
| com.hazelcast.spi.discovery.multicast.MulticastDiscoveryStrategyTest | |
| com.hazelcast.spi.utils.RestClientTest | |
| com.hazelcast.spi.utils.RetryUtilsTest | |
| '''.split() | |
| expected = [ | |
| (pathlib.Path('hazelcast/target/surefire-reports'), name) | |
| for name in core | |
| ] | |
| expected.append(( | |
| pathlib.Path('hazelcast-sql/target/surefire-reports'), | |
| 'com.hazelcast.jet.sql.impl.parse.QueryParserTest', | |
| )) | |
| expected.append(( | |
| pathlib.Path('hazelcast-build-utils/target/surefire-reports'), | |
| 'com.hazelcast.buildutils.ExportPackageViewerTest', | |
| )) | |
| expected.append(( | |
| pathlib.Path('extensions/cdc-debezium/target/surefire-reports'), | |
| 'com.hazelcast.jet.cdc.OperationTest', | |
| )) | |
| total = failures = errors = skipped = 0 | |
| for directory, name in expected: | |
| report = directory / f'TEST-{name}.xml' | |
| if not report.is_file(): | |
| raise SystemExit(f'missing_test_report={name}') | |
| suite = ET.parse(report).getroot() | |
| total += int(suite.attrib.get('tests', 0)) | |
| failures += int(suite.attrib.get('failures', 0)) | |
| errors += int(suite.attrib.get('errors', 0)) | |
| skipped += int(suite.attrib.get('skipped', 0)) | |
| summary = ( | |
| f'test_suites={len(expected)}\n' | |
| f'tests={total}\nfailures={failures}\nerrors={errors}\nskipped={skipped}\n' | |
| ) | |
| pathlib.Path('evidence/test-summary.txt').write_text(summary, encoding='utf-8') | |
| print(summary, end='') | |
| if total < 385 or failures or errors or skipped: | |
| raise SystemExit(1) | |
| PY | |
| - name: Build reviewed runtime artifact | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| PASTURESTACK_OUTPUT_DIR=dist scripts/pasturestack-build-runtime | |
| test -s dist/hazelcast-5.7.3-pasturestack.2.jar | |
| sha256sum dist/hazelcast-5.7.3-pasturestack.2.jar | tee evidence/hazelcast-5.7.3-pasturestack.2.jar.sha256 | |
| jar tf dist/hazelcast-5.7.3-pasturestack.2.jar | grep -Fxq META-INF/LICENSE | |
| jar tf dist/hazelcast-5.7.3-pasturestack.2.jar | grep -Fxq META-INF/NOTICE | |
| native_resources=( | |
| hazelcast/src/main/resources/affinity_helper.c | |
| hazelcast/src/main/resources/affinity_helper.h | |
| hazelcast/src/main/resources/icmp_helper.c | |
| hazelcast/src/main/resources/icmp_helper.h | |
| hazelcast-tpc-engine/src/main/resources/lib/linux-x86/libicmp_helper.so | |
| hazelcast-tpc-engine/src/main/resources/lib/linux-x86_64/libaffinity_helper.so | |
| hazelcast-tpc-engine/src/main/resources/lib/linux-x86_64/libicmp_helper.so | |
| ) | |
| sha256sum "${native_resources[@]}" > evidence/native-resources.sha256 | |
| extracted_native=$(mktemp -d) | |
| trap 'rm -rf "$extracted_native"' EXIT | |
| ( | |
| cd "$extracted_native" | |
| jar xf "$GITHUB_WORKSPACE/dist/hazelcast-5.7.3-pasturestack.2.jar" \ | |
| lib/linux-x86/libicmp_helper.so \ | |
| lib/linux-x86_64/libaffinity_helper.so \ | |
| lib/linux-x86_64/libicmp_helper.so | |
| ) | |
| cmp hazelcast-tpc-engine/src/main/resources/lib/linux-x86/libicmp_helper.so \ | |
| "$extracted_native/lib/linux-x86/libicmp_helper.so" | |
| cmp hazelcast-tpc-engine/src/main/resources/lib/linux-x86_64/libaffinity_helper.so \ | |
| "$extracted_native/lib/linux-x86_64/libaffinity_helper.so" | |
| cmp hazelcast-tpc-engine/src/main/resources/lib/linux-x86_64/libicmp_helper.so \ | |
| "$extracted_native/lib/linux-x86_64/libicmp_helper.so" | |
| if jar tf dist/hazelcast-5.7.3-pasturestack.2.jar | grep -Eq '(^|/)(jet_to_python[^/]*|[^/]+\.py)$'; then | |
| echo 'The core runtime artifact unexpectedly contains Python extension resources.' >&2 | |
| exit 1 | |
| fi | |
| printf 'native_resources_rebuilt=0\nnative_packaged_mismatch=0\npython_extension_resources=0\n' \ | |
| > evidence/runtime-toolchain-boundary.txt | |
| - name: Generate SBOM and scan current source and artifact | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| # Generate the reviewed runtime SBOM from Maven's resolved dependency | |
| # graph. A filesystem scanner can only see this project's shaded JAR | |
| # as one file and therefore cannot reconstruct its embedded libraries. | |
| evidence_plugin_stage="$RUNNER_TEMP/maven-evidence-plugins" | |
| mkdir -p "$evidence_plugin_stage" | |
| stage_evidence_plugin() | |
| { | |
| local artifact=$1 | |
| local version=$2 | |
| local jar_sha256=$3 | |
| local pom_sha256=$4 | |
| local plugin_stage="$evidence_plugin_stage/$artifact" | |
| local plugin_repo="$HOME/.m2/repository/org/apache/maven/plugins/$artifact/$version" | |
| local plugin_base="https://repo.maven.apache.org/maven2/org/apache/maven/plugins/$artifact/$version" | |
| mkdir -p "$plugin_stage" "$plugin_repo" | |
| curl --proto '=https' --tlsv1.2 --fail --silent --show-error --location \ | |
| --output "$plugin_stage/$artifact-$version.jar" \ | |
| "$plugin_base/$artifact-$version.jar" | |
| curl --proto '=https' --tlsv1.2 --fail --silent --show-error --location \ | |
| --output "$plugin_stage/$artifact-$version.pom" \ | |
| "$plugin_base/$artifact-$version.pom" | |
| printf '%s %s\n%s %s\n' \ | |
| "$jar_sha256" "$plugin_stage/$artifact-$version.jar" \ | |
| "$pom_sha256" "$plugin_stage/$artifact-$version.pom" | sha256sum -c - | |
| install -m 0644 "$plugin_stage/$artifact-$version.jar" "$plugin_repo/" | |
| install -m 0644 "$plugin_stage/$artifact-$version.pom" "$plugin_repo/" | |
| ( | |
| cd "$plugin_stage" | |
| sha256sum "$artifact-$version.jar" "$artifact-$version.pom" | |
| ) >> "$GITHUB_WORKSPACE/evidence/evidence-tools.sha256" | |
| } | |
| stage_evidence_plugin \ | |
| maven-help-plugin 3.5.2 \ | |
| fe53082c6c51b9b4778f4dfe3e9810c18e995c34b0780c30b73fb2a156c90c03 \ | |
| 8afe7663b3150965bbdb27a806f325a748268922491d23cd552e78851dea32ea | |
| stage_evidence_plugin \ | |
| maven-dependency-plugin 3.11.0 \ | |
| e37a8fc9ff47435cc6bda257a4501cf79626c8115610ba33c77775ce23b4c3b7 \ | |
| 570b5e43230f7a13f9ec8ae0b0993208a5f4c763b6593a87af4123112ce47220 | |
| plugin_stage="$RUNNER_TEMP/cyclonedx-maven-plugin" | |
| plugin_repo="$HOME/.m2/repository/org/cyclonedx/cyclonedx-maven-plugin/2.9.3" | |
| plugin_base=https://repo.maven.apache.org/maven2/org/cyclonedx/cyclonedx-maven-plugin/2.9.3 | |
| mkdir -p "$plugin_stage" "$plugin_repo" | |
| curl --proto '=https' --tlsv1.2 --fail --silent --show-error --location \ | |
| --output "$plugin_stage/cyclonedx-maven-plugin-2.9.3.jar" \ | |
| "$plugin_base/cyclonedx-maven-plugin-2.9.3.jar" | |
| curl --proto '=https' --tlsv1.2 --fail --silent --show-error --location \ | |
| --output "$plugin_stage/cyclonedx-maven-plugin-2.9.3.pom" \ | |
| "$plugin_base/cyclonedx-maven-plugin-2.9.3.pom" | |
| printf '%s %s\n%s %s\n' \ | |
| c452d5eebe28bc86bef2e7c72d129f04f60877bef843eac8120f01fb655be293 \ | |
| "$plugin_stage/cyclonedx-maven-plugin-2.9.3.jar" \ | |
| 7c8d3c7c1a2efd58c134f09926f3de47d3de26de36130775ab1c3bfb74b4279c \ | |
| "$plugin_stage/cyclonedx-maven-plugin-2.9.3.pom" | sha256sum -c - | |
| install -m 0644 "$plugin_stage/cyclonedx-maven-plugin-2.9.3.jar" "$plugin_repo/" | |
| install -m 0644 "$plugin_stage/cyclonedx-maven-plugin-2.9.3.pom" "$plugin_repo/" | |
| ( | |
| cd "$plugin_stage" | |
| sha256sum cyclonedx-maven-plugin-2.9.3.jar cyclonedx-maven-plugin-2.9.3.pom | |
| ) >> "$GITHUB_WORKSPACE/evidence/evidence-tools.sha256" | |
| # The reviewed artifact build above already compiled the reactor. | |
| # Install only the POMs and runtime prerequisite JAR into the runner's | |
| # ephemeral repository, without recompiling or running tests. | |
| ./mvnw -B org.apache.maven.plugins:maven-install-plugin:3.1.4:install-file \ | |
| -Dfile=pom.xml -DpomFile=pom.xml | |
| ./mvnw -B org.apache.maven.plugins:maven-install-plugin:3.1.4:install-file \ | |
| -Dfile=hazelcast-parent/pom.xml -DpomFile=hazelcast-parent/pom.xml | |
| test -s hazelcast-tpc-engine/target/hazelcast-tpc-engine-5.7.3-pasturestack.2.jar | |
| ./mvnw -B org.apache.maven.plugins:maven-install-plugin:3.1.4:install-file \ | |
| -Dfile=hazelcast-tpc-engine/target/hazelcast-tpc-engine-5.7.3-pasturestack.2.jar \ | |
| -DpomFile=hazelcast-tpc-engine/pom.xml | |
| timeout --signal=TERM --kill-after=30s 15m ./mvnw -B \ | |
| org.apache.maven.plugins:maven-help-plugin:3.5.2:effective-pom \ | |
| -Doutput="$PWD/evidence/maven-effective-pom.xml" | |
| test -s evidence/maven-effective-pom.xml | |
| timeout --signal=TERM --kill-after=30s 30m ./mvnw -B \ | |
| -f hazelcast/pom.xml \ | |
| -DskipTests \ | |
| -Dcyclonedx.skipAttach=true \ | |
| -DincludeBomSerialNumber=false \ | |
| -DincludeCompileScope=true \ | |
| -DincludeProvidedScope=false \ | |
| -DincludeRuntimeScope=true \ | |
| -DincludeSystemScope=false \ | |
| -DincludeTestScope=false \ | |
| -DoutputFormat=json \ | |
| -DoutputReactorProjects=false \ | |
| -DoutputDirectory="$PWD/evidence" \ | |
| -DoutputName=hazelcast-5.7.3-pasturestack.2.cdx \ | |
| -DschemaVersion=1.6 \ | |
| org.cyclonedx:cyclonedx-maven-plugin:2.9.3:makeBom | |
| printf '%s %s\n%s %s\n' \ | |
| c452d5eebe28bc86bef2e7c72d129f04f60877bef843eac8120f01fb655be293 \ | |
| "$plugin_repo/cyclonedx-maven-plugin-2.9.3.jar" \ | |
| 7c8d3c7c1a2efd58c134f09926f3de47d3de26de36130775ab1c3bfb74b4279c \ | |
| "$plugin_repo/cyclonedx-maven-plugin-2.9.3.pom" | sha256sum -c - | |
| printf '%s %s\n%s %s\n%s %s\n%s %s\n' \ | |
| fe53082c6c51b9b4778f4dfe3e9810c18e995c34b0780c30b73fb2a156c90c03 \ | |
| "$HOME/.m2/repository/org/apache/maven/plugins/maven-help-plugin/3.5.2/maven-help-plugin-3.5.2.jar" \ | |
| 8afe7663b3150965bbdb27a806f325a748268922491d23cd552e78851dea32ea \ | |
| "$HOME/.m2/repository/org/apache/maven/plugins/maven-help-plugin/3.5.2/maven-help-plugin-3.5.2.pom" \ | |
| e37a8fc9ff47435cc6bda257a4501cf79626c8115610ba33c77775ce23b4c3b7 \ | |
| "$HOME/.m2/repository/org/apache/maven/plugins/maven-dependency-plugin/3.11.0/maven-dependency-plugin-3.11.0.jar" \ | |
| 570b5e43230f7a13f9ec8ae0b0993208a5f4c763b6593a87af4123112ce47220 \ | |
| "$HOME/.m2/repository/org/apache/maven/plugins/maven-dependency-plugin/3.11.0/maven-dependency-plugin-3.11.0.pom" | sha256sum -c - | |
| timeout --signal=TERM --kill-after=30s 10m ./mvnw -B \ | |
| -f hazelcast/pom.xml \ | |
| -Dscope=runtime \ | |
| -DoutputType=json \ | |
| -DoutputFile="$PWD/evidence/maven-runtime-dependency-tree.json" \ | |
| org.apache.maven.plugins:maven-dependency-plugin:3.11.0:tree | |
| docker pull "$TRIVY_IMAGE" | |
| trivy_cache="$RUNNER_TEMP/trivy-cache" | |
| mkdir -p "$trivy_cache" | |
| docker run --rm \ | |
| -v "$trivy_cache:/root/.cache/trivy" \ | |
| "$TRIVY_IMAGE" image --cache-dir /root/.cache/trivy --download-db-only | |
| docker run --rm \ | |
| -v "$trivy_cache:/root/.cache/trivy" \ | |
| "$TRIVY_IMAGE" image --cache-dir /root/.cache/trivy --download-java-db-only | |
| source_tree=$(mktemp -d) | |
| trap 'rm -rf "$source_tree"' EXIT | |
| git archive --format=tar HEAD | tar -xf - -C "$source_tree" | |
| docker run --rm --network none \ | |
| -v "$source_tree:/scan:ro" \ | |
| -v "$PWD/evidence:/evidence" \ | |
| -v "$trivy_cache:/root/.cache/trivy" \ | |
| "$TRIVY_IMAGE" fs \ | |
| --cache-dir /root/.cache/trivy --skip-db-update --offline-scan \ | |
| --scanners vuln,secret --severity CRITICAL,HIGH --format json \ | |
| --output /evidence/source-security.json /scan | |
| docker run --rm --network none \ | |
| -v "$PWD/dist:/artifact:ro" \ | |
| -v "$PWD/evidence:/evidence" \ | |
| -v "$trivy_cache:/root/.cache/trivy" \ | |
| "$TRIVY_IMAGE" rootfs --pkg-types library \ | |
| --cache-dir /root/.cache/trivy --skip-db-update --offline-scan \ | |
| --scanners vuln --severity CRITICAL,HIGH --format json \ | |
| --output /evidence/hazelcast-5.7.3-pasturestack.2.trivy.json /artifact/hazelcast-5.7.3-pasturestack.2.jar | |
| docker run --rm --network none \ | |
| -v "$PWD/evidence:/evidence" \ | |
| -v "$trivy_cache:/root/.cache/trivy" \ | |
| "$TRIVY_IMAGE" sbom \ | |
| --cache-dir /root/.cache/trivy --skip-db-update --skip-java-db-update --offline-scan \ | |
| --scanners vuln --severity CRITICAL,HIGH --format json \ | |
| --output /evidence/hazelcast-5.7.3-pasturestack.2.sbom.trivy.json \ | |
| /evidence/hazelcast-5.7.3-pasturestack.2.cdx.json | |
| sha256sum evidence/maven-effective-pom.xml \ | |
| evidence/maven-runtime-dependency-tree.json evidence/hazelcast-5.7.3-pasturestack.2.cdx.json \ | |
| evidence/hazelcast-5.7.3-pasturestack.2.sbom.trivy.json evidence/hazelcast-5.7.3-pasturestack.2.trivy.json \ | |
| evidence/evidence-tools.sha256 \ | |
| evidence/native-resources.sha256 evidence/runtime-toolchain-boundary.txt \ | |
| > evidence/security-evidence.sha256 | |
| - name: Enforce complete zero-Critical and zero-High evidence | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| python scripts/pasturestack-verify-maven-evidence.py \ | |
| --effective-pom evidence/maven-effective-pom.xml \ | |
| --sbom evidence/hazelcast-5.7.3-pasturestack.2.cdx.json \ | |
| --dependency-tree evidence/maven-runtime-dependency-tree.json \ | |
| --summary evidence/maven-evidence-summary.txt | |
| sha256sum evidence/maven-evidence-summary.txt >> evidence/security-evidence.sha256 | |
| python3 - <<'PY' | |
| import json | |
| import pathlib | |
| def load(path): | |
| with open(path, encoding='utf-8') as stream: | |
| return json.load(stream) | |
| source = load('evidence/source-security.json') | |
| artifact = load('evidence/hazelcast-5.7.3-pasturestack.2.trivy.json') | |
| dependency_scan = load('evidence/hazelcast-5.7.3-pasturestack.2.sbom.trivy.json') | |
| sbom = load('evidence/hazelcast-5.7.3-pasturestack.2.cdx.json') | |
| maven_tree = load('evidence/maven-runtime-dependency-tree.json') | |
| source_vulnerabilities = [ | |
| item | |
| for result in source.get('Results', []) | |
| for item in result.get('Vulnerabilities') or [] | |
| if item.get('Severity') in {'CRITICAL', 'HIGH'} | |
| ] | |
| source_secrets = [ | |
| item | |
| for result in source.get('Results', []) | |
| for item in result.get('Secrets') or [] | |
| ] | |
| artifact_vulnerabilities = [ | |
| item | |
| for result in artifact.get('Results', []) | |
| for item in result.get('Vulnerabilities') or [] | |
| if item.get('Severity') in {'CRITICAL', 'HIGH'} | |
| ] | |
| dependency_vulnerabilities = [ | |
| item | |
| for result in dependency_scan.get('Results', []) | |
| for item in result.get('Vulnerabilities') or [] | |
| if item.get('Severity') in {'CRITICAL', 'HIGH'} | |
| ] | |
| components = sbom.get('components') or [] | |
| component_refs = [item.get('bom-ref') for item in components] | |
| component_purls = [item.get('purl') for item in components] | |
| dependencies = sbom.get('dependencies') or [] | |
| root_component = sbom.get('metadata', {}).get('component') or {} | |
| root_ref = root_component.get('bom-ref') | |
| dependency_refs = {item.get('ref') for item in dependencies} | |
| allowed_refs = set(component_refs) | {root_ref} | |
| dangling_dependency_refs = sorted( | |
| child | |
| for item in dependencies | |
| for child in item.get('dependsOn') or [] | |
| if child not in allowed_refs | |
| ) | |
| dependencies_exist = any(item.get('dependsOn') for item in dependencies) | |
| maven_coordinates = set() | |
| remaining_nodes = [maven_tree] | |
| while remaining_nodes: | |
| node = remaining_nodes.pop() | |
| if node.get('scope') in {'compile', 'runtime'} and str(node.get('optional')).lower() != 'true': | |
| maven_coordinates.add((node.get('groupId'), node.get('artifactId'), node.get('version'))) | |
| remaining_nodes.extend(node.get('children') or []) | |
| sbom_coordinates = { | |
| (item.get('group'), item.get('name'), item.get('version')) | |
| for item in components | |
| } | |
| maven_root = (maven_tree.get('groupId'), maven_tree.get('artifactId'), maven_tree.get('version')) | |
| sbom_root = (root_component.get('group'), root_component.get('name'), root_component.get('version')) | |
| missing_from_sbom = sorted(maven_coordinates - sbom_coordinates) | |
| unexpected_in_sbom = sorted(sbom_coordinates - maven_coordinates) | |
| missing_identity = [ | |
| item for item in components | |
| if not item.get('group') or not item.get('name') or not item.get('version') or not item.get('purl') | |
| ] | |
| summary = ( | |
| f'source_critical_high={len(source_vulnerabilities)}\n' | |
| f'source_secrets={len(source_secrets)}\n' | |
| f'artifact_critical_high={len(artifact_vulnerabilities)}\n' | |
| f'dependency_critical_high={len(dependency_vulnerabilities)}\n' | |
| f'sbom_spec={sbom.get("specVersion", "missing")}\n' | |
| f'sbom_components={len(components)}\n' | |
| f'sbom_dependencies={len(dependencies)}\n' | |
| f'maven_runtime_components={len(maven_coordinates)}\n' | |
| f'sbom_runtime_missing={len(missing_from_sbom)}\n' | |
| f'sbom_runtime_unexpected={len(unexpected_in_sbom)}\n' | |
| f'sbom_dangling_dependency_refs={len(dangling_dependency_refs)}\n' | |
| ) | |
| pathlib.Path('evidence/security-summary.txt').write_text(summary, encoding='utf-8') | |
| print(summary, end='') | |
| if ( | |
| source_vulnerabilities | |
| or source_secrets | |
| or artifact_vulnerabilities | |
| or dependency_vulnerabilities | |
| or sbom.get('specVersion') != '1.6' | |
| or not components | |
| or missing_identity | |
| or None in component_refs | |
| or None in component_purls | |
| or len(component_refs) != len(set(component_refs)) | |
| or len(component_purls) != len(set(component_purls)) | |
| or maven_root != ('com.hazelcast', 'hazelcast', '5.7.3-pasturestack.2') | |
| or sbom_root != maven_root | |
| or root_ref not in dependency_refs | |
| or dependency_refs != allowed_refs | |
| or dangling_dependency_refs | |
| or not dependencies_exist | |
| or missing_from_sbom | |
| or unexpected_in_sbom | |
| ): | |
| raise SystemExit(1) | |
| PY | |
| sha256sum evidence/security-summary.txt >> evidence/security-evidence.sha256 | |
| # The Maven plugin validates the produced BOM against CycloneDX 1.6 | |
| # while writing it; this second independent validator prevents a | |
| # generator regression from being accepted by its own implementation. | |
| docker run --rm --network none \ | |
| -v "$PWD/evidence:/evidence:ro" \ | |
| cyclonedx/cyclonedx-cli:0.33.1@sha256:252c2e26f468c25fea1e63ecde1bc3198ad6e9dbb57f5ed3236bddcb2281b3a7 \ | |
| validate --input-file /evidence/hazelcast-5.7.3-pasturestack.2.cdx.json \ | |
| --input-format json --input-version v1_6 --fail-on-errors | |
| - name: Upload review evidence | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| with: | |
| name: distributed-cache-security-${{ github.sha }} | |
| path: evidence/ | |
| if-no-files-found: error | |
| retention-days: 30 | |
| compression-level: 9 | |
| include-hidden-files: false | |
| - name: Clean runner resources | |
| if: always() | |
| shell: bash | |
| run: | | |
| set +e | |
| docker image rm -f "$TRIVY_IMAGE" >/dev/null 2>&1 | |
| docker builder prune --all --force >/dev/null 2>&1 | |
| rm -rf dist evidence |