-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add automated test workflow for certificate validation #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8f4e324
889e6de
bc43803
e05b006
b4c3503
fc62dad
38d8964
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,274 @@ | ||
| # | ||
| # Copyright 2026 ABSA Group Limited | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| # | ||
|
|
||
| name: Test | ||
|
|
||
| on: | ||
| pull_request: | ||
| branches: [ master ] | ||
| push: | ||
| branches: [ master ] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| test: | ||
| name: Test (${{ matrix.os }}) | ||
| runs-on: ${{ matrix.os }} | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| os: [ubuntu-latest, macos-latest] | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| # Action uses bash 4+ features (mapfile, associative arrays). | ||
| # macOS runners ship bash 3.2 — install a modern version via Homebrew. | ||
| - name: Install bash (macOS) | ||
| if: runner.os == 'macOS' | ||
| run: brew install bash | ||
|
|
||
| - name: Generate test fixtures | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| FIXTURES_DIR="tests/fixtures" | ||
| CA_DIR="$(pwd)/$FIXTURES_DIR/ca" | ||
| mkdir -p "$CA_DIR/newcerts" | ||
| touch "$CA_DIR/index.txt" | ||
| echo "1000" > "$CA_DIR/serial" | ||
|
|
||
| cat > "$CA_DIR/openssl.cnf" << EOF | ||
| [ca] | ||
| default_ca = test_ca | ||
| [test_ca] | ||
| dir = ${CA_DIR} | ||
| database = \$dir/index.txt | ||
| new_certs_dir = \$dir/newcerts | ||
| serial = \$dir/serial | ||
| certificate = \$dir/ca.pem | ||
| private_key = \$dir/ca.key | ||
| default_md = sha256 | ||
| policy = policy_any | ||
| unique_subject = no | ||
| [policy_any] | ||
| commonName = supplied | ||
| [req] | ||
| distinguished_name = req_dn | ||
| prompt = no | ||
| [req_dn] | ||
| EOF | ||
|
|
||
| openssl req -x509 -newkey rsa:2048 -keyout "$CA_DIR/ca.key" -out "$CA_DIR/ca.pem" \ | ||
| -days 7300 -nodes -subj "/CN=Test CA" 2>/dev/null | ||
|
|
||
| sign_cert() { | ||
| local out="$1" cn="$2" startdate="$3" enddate="$4" | ||
| local tmpkey tmpcsr | ||
| tmpkey=$(mktemp) | ||
| tmpcsr=$(mktemp) | ||
| openssl req -newkey rsa:2048 -keyout "$tmpkey" -out "$tmpcsr" \ | ||
| -nodes -subj "/CN=$cn" 2>/dev/null | ||
| openssl ca -batch -config "$CA_DIR/openssl.cnf" \ | ||
| -startdate "$startdate" -enddate "$enddate" \ | ||
| -in "$tmpcsr" -out "$out" -notext 2>/dev/null | ||
| rm -f "$tmpkey" "$tmpcsr" | ||
| } | ||
|
|
||
| YEAR=$(date -u +%Y) | ||
| YY_2AGO=$(printf "%02d" $(( (YEAR - 2) % 100 ))) | ||
| YY_1AGO=$(printf "%02d" $(( (YEAR - 1) % 100 ))) | ||
| YY_1AHEAD=$(printf "%02d" $(( (YEAR + 1) % 100 ))) | ||
| YY_2AHEAD=$(printf "%02d" $(( (YEAR + 2) % 100 ))) | ||
|
|
||
| openssl req -x509 -newkey rsa:2048 -keyout /dev/null \ | ||
| -out "$FIXTURES_DIR/valid.pem" -days 365 -nodes \ | ||
| -subj "/CN=valid.example.com" 2>/dev/null | ||
|
|
||
| openssl req -x509 -newkey rsa:2048 -keyout /dev/null \ | ||
| -out "$FIXTURES_DIR/expiring.pem" -days 10 -nodes \ | ||
| -subj "/CN=expiring.example.com" 2>/dev/null | ||
|
|
||
| sign_cert "$FIXTURES_DIR/expired.pem" "expired.example.com" \ | ||
| "${YY_2AGO}0101000000Z" "${YY_1AGO}0101000000Z" | ||
|
|
||
| sign_cert "$FIXTURES_DIR/not_yet_valid.pem" "notyetvalid.example.com" \ | ||
| "${YY_1AHEAD}0101000000Z" "${YY_2AHEAD}0101000000Z" | ||
|
|
||
| sign_cert "$FIXTURES_DIR/multi_expired.pem" "multi.example.com" \ | ||
| "${YY_2AGO}0101000000Z" "${YY_1AGO}0101000000Z" | ||
|
|
||
| openssl req -x509 -newkey rsa:2048 -keyout /dev/null \ | ||
| -out "$FIXTURES_DIR/multi_valid.pem" -days 365 -nodes \ | ||
| -subj "/CN=multi.example.com" 2>/dev/null | ||
|
|
||
| # Expiring-soon cert sharing a subject with multi_valid.pem, used to | ||
| # prove fail_on_warn does not fail when a newer valid replacement exists. | ||
| openssl req -x509 -newkey rsa:2048 -keyout /dev/null \ | ||
| -out "$FIXTURES_DIR/multi_expiring.pem" -days 10 -nodes \ | ||
| -subj "/CN=multi.example.com" 2>/dev/null | ||
|
|
||
| echo "Fixtures generated:" | ||
| ls -la "$FIXTURES_DIR"/*.pem | ||
|
|
||
| - name: 'Test: valid certificate' | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks awesome overrall, one thing i spotted is that the case for fail_on_warn test case is not added, another one is that we do not test if given certs are valid JSON. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've added those test cases, it should be complete now |
||
| shell: bash | ||
| run: | | ||
| SUMMARY=$(mktemp) | ||
| INPUT_CERTIFICATES='["tests/fixtures/valid.pem"]' \ | ||
| INPUT_WARNING_DAYS=30 \ | ||
| GITHUB_STEP_SUMMARY="$SUMMARY" \ | ||
| bash validate.sh | ||
| grep -q "✅ Valid" "$SUMMARY" | ||
| rm -f "$SUMMARY" | ||
|
|
||
| - name: 'Test: expiring-soon certificate' | ||
| shell: bash | ||
| run: | | ||
| SUMMARY=$(mktemp) | ||
| INPUT_CERTIFICATES='["tests/fixtures/expiring.pem"]' \ | ||
| INPUT_WARNING_DAYS=30 \ | ||
| GITHUB_STEP_SUMMARY="$SUMMARY" \ | ||
| bash validate.sh | ||
| grep -q "Expiring Soon" "$SUMMARY" | ||
| rm -f "$SUMMARY" | ||
|
|
||
| - name: 'Test: expired certificate' | ||
| shell: bash | ||
| run: | | ||
| SUMMARY=$(mktemp) | ||
| EXIT=0 | ||
| INPUT_CERTIFICATES='["tests/fixtures/expired.pem"]' \ | ||
| INPUT_WARNING_DAYS=30 \ | ||
| GITHUB_STEP_SUMMARY="$SUMMARY" \ | ||
| bash validate.sh || EXIT=$? | ||
| if [[ "$EXIT" -ne 1 ]]; then | ||
| echo "::error::Expected exit code 1 for expired cert, got $EXIT" | ||
| exit 1 | ||
| fi | ||
| grep -q "Expired" "$SUMMARY" | ||
| rm -f "$SUMMARY" | ||
|
|
||
| - name: 'Test: not-yet-valid certificate' | ||
| shell: bash | ||
| run: | | ||
| SUMMARY=$(mktemp) | ||
| EXIT=0 | ||
| INPUT_CERTIFICATES='["tests/fixtures/not_yet_valid.pem"]' \ | ||
| INPUT_WARNING_DAYS=30 \ | ||
| GITHUB_STEP_SUMMARY="$SUMMARY" \ | ||
| bash validate.sh || EXIT=$? | ||
| if [[ "$EXIT" -ne 1 ]]; then | ||
| echo "::error::Expected exit code 1 for not-yet-valid cert, got $EXIT" | ||
| exit 1 | ||
| fi | ||
| grep -q "Failed" "$SUMMARY" | ||
| rm -f "$SUMMARY" | ||
|
|
||
| - name: 'Test: missing file' | ||
| shell: bash | ||
| run: | | ||
| SUMMARY=$(mktemp) | ||
| EXIT=0 | ||
| INPUT_CERTIFICATES='["/nonexistent/cert.pem"]' \ | ||
| INPUT_WARNING_DAYS=30 \ | ||
| GITHUB_STEP_SUMMARY="$SUMMARY" \ | ||
| bash validate.sh || EXIT=$? | ||
| if [[ "$EXIT" -ne 1 ]]; then | ||
| echo "::error::Expected exit code 1 for missing file, got $EXIT" | ||
| exit 1 | ||
| fi | ||
| grep -q "Failed" "$SUMMARY" | ||
| rm -f "$SUMMARY" | ||
|
|
||
| - name: 'Test: multi-cert with valid replacement' | ||
| shell: bash | ||
| run: | | ||
| SUMMARY=$(mktemp) | ||
| INPUT_CERTIFICATES='["tests/fixtures/multi_expired.pem", "tests/fixtures/multi_valid.pem"]' \ | ||
| INPUT_WARNING_DAYS=30 \ | ||
| GITHUB_STEP_SUMMARY="$SUMMARY" \ | ||
| bash validate.sh | ||
| grep -q "newer replacement exists" "$SUMMARY" | ||
| rm -f "$SUMMARY" | ||
|
|
||
| - name: 'Test: fail_on_warn fails on expiring-soon certificate' | ||
| shell: bash | ||
| run: | | ||
| SUMMARY=$(mktemp) | ||
| EXIT=0 | ||
| INPUT_CERTIFICATES='["tests/fixtures/expiring.pem"]' \ | ||
| INPUT_WARNING_DAYS=30 \ | ||
| INPUT_FAIL_ON_WARN=true \ | ||
| GITHUB_STEP_SUMMARY="$SUMMARY" \ | ||
| bash validate.sh || EXIT=$? | ||
| if [[ "$EXIT" -ne 1 ]]; then | ||
| echo "::error::Expected exit code 1 for fail_on_warn=true with expiring cert, got $EXIT" | ||
| exit 1 | ||
| fi | ||
| grep -q "Expiring Soon" "$SUMMARY" | ||
| rm -f "$SUMMARY" | ||
|
|
||
| - name: 'Test: fail_on_warn does not fail when expiring cert has valid replacement' | ||
| shell: bash | ||
| run: | | ||
| SUMMARY=$(mktemp) | ||
| INPUT_CERTIFICATES='["tests/fixtures/multi_expiring.pem", "tests/fixtures/multi_valid.pem"]' \ | ||
| INPUT_WARNING_DAYS=30 \ | ||
| INPUT_FAIL_ON_WARN=true \ | ||
| GITHUB_STEP_SUMMARY="$SUMMARY" \ | ||
| bash validate.sh | ||
| # Must NOT fail (newer valid replacement exists, so WARN_COUNT stays 0), | ||
| # and the summary status must reflect the nearing-expiry cert. | ||
| grep -q "Expiring Soon (newer replacement exists)" "$SUMMARY" | ||
| rm -f "$SUMMARY" | ||
|
|
||
| - name: 'Test: invalid JSON input' | ||
| shell: bash | ||
| run: | | ||
| OUTPUT=$(mktemp) | ||
| EXIT=0 | ||
| INPUT_CERTIFICATES='not-valid-json' \ | ||
| INPUT_WARNING_DAYS=30 \ | ||
| bash validate.sh > "$OUTPUT" 2>&1 || EXIT=$? | ||
| if [[ "$EXIT" -ne 1 ]]; then | ||
| echo "::error::Expected exit code 1 for invalid JSON input, got $EXIT" | ||
| cat "$OUTPUT" | ||
| exit 1 | ||
| fi | ||
| grep -q "must be a valid JSON array" "$OUTPUT" | ||
| rm -f "$OUTPUT" | ||
|
|
||
| - name: 'Test: valid JSON but not an array' | ||
| shell: bash | ||
| run: | | ||
| OUTPUT=$(mktemp) | ||
| EXIT=0 | ||
| INPUT_CERTIFICATES='{"cert": "tests/fixtures/valid.pem"}' \ | ||
| INPUT_WARNING_DAYS=30 \ | ||
| bash validate.sh > "$OUTPUT" 2>&1 || EXIT=$? | ||
| if [[ "$EXIT" -ne 1 ]]; then | ||
| echo "::error::Expected exit code 1 for non-array JSON input, got $EXIT" | ||
| cat "$OUTPUT" | ||
| exit 1 | ||
| fi | ||
| grep -q "must be a valid JSON array" "$OUTPUT" | ||
| rm -f "$OUTPUT" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,3 +8,6 @@ Thumbs.db | |
|
|
||
| # Local test scripts | ||
| run_locally.sh | ||
|
|
||
| # Generated test fixtures | ||
| tests/fixtures/ | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even the referenced code clearly says
actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd, nothing to fix here