mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
ci(ocr): verify the baked trust file, not unset image env vars (#666)
Fourth latent bug in the OCR publish chain (#649/#519 added it, never ran). verify-ocr's trust-identity step read OCR_RUNTIME_INDEX_KEY_ID from the image env, which the official image leaves unset by design (operator-override path; runtime-index.ts reads the baked file when env is unset). It compared "" to snapotter-ocr-2026-07 and failed a correct image. Now verifies the baked /app/docker/ocr-runtime-trust.json, proven against the built release image. A full audit of the remaining chain (verify, sign, verify-signed, publish) reproduced the sign+verify path end-to-end inside the image and confirmed nothing else breaks for v2.2.0. One latent non-ASCII canonicalization mismatch filed as #667 (cannot fire on v2.2.0). Non-releasable type so the re-dispatch re-runs 2.2.0.
This commit is contained in:
@@ -358,18 +358,25 @@ jobs:
|
||||
run: |
|
||||
: "${OCR_RUNTIME_INDEX_KEY_ID:?Set repository variable OCR_RUNTIME_INDEX_KEY_ID}"
|
||||
: "${OCR_RUNTIME_INDEX_PUBLIC_KEY_PEM_B64:?Set repository variable OCR_RUNTIME_INDEX_PUBLIC_KEY_PEM_B64}"
|
||||
actual_key_id="$(docker run --rm --entrypoint sh "${SNAPOTTER_BUNDLE_IMAGE}" \
|
||||
-c 'printf %s "$OCR_RUNTIME_INDEX_KEY_ID"')"
|
||||
actual_public_key="$(docker run --rm --entrypoint sh "${SNAPOTTER_BUNDLE_IMAGE}" \
|
||||
-c 'printf %s "$OCR_RUNTIME_INDEX_PUBLIC_KEY_PEM_B64"')"
|
||||
# The official image bakes the trust store as a FILE
|
||||
# (/app/docker/ocr-runtime-trust.json, written by write-ocr-runtime-trust.mjs)
|
||||
# and deliberately leaves OCR_RUNTIME_INDEX_* unset in the image env: those
|
||||
# are the operator-override path, and loadOcrRuntimeTrustKeys
|
||||
# (packages/ai/src/runtime-index.ts:52-71) reads env only when set, else the
|
||||
# baked file. So verify the file the runtime actually trusts, not the env.
|
||||
trust_json="$(docker run --rm --entrypoint sh "${SNAPOTTER_BUNDLE_IMAGE}" \
|
||||
-c 'cat /app/docker/ocr-runtime-trust.json')"
|
||||
actual_key_id="$(printf '%s' "${trust_json}" | jq -r '.keys[0].keyId')"
|
||||
actual_public_key="$(printf '%s' "${trust_json}" | jq -r '.keys[0].publicKey')"
|
||||
expected_public_key="$(printf '%s' "${OCR_RUNTIME_INDEX_PUBLIC_KEY_PEM_B64}" | base64 --decode)"
|
||||
actual_official_container="$(docker run --rm --entrypoint sh "${SNAPOTTER_BUNDLE_IMAGE}" \
|
||||
-c 'printf %s "$SNAPOTTER_OFFICIAL_CONTAINER"')"
|
||||
[[ "${actual_key_id}" == "${OCR_RUNTIME_INDEX_KEY_ID}" ]] || {
|
||||
echo "::error::Verification image OCR key ID does not match the release identity"
|
||||
echo "::error::Baked OCR trust key ID (${actual_key_id}) does not match the release identity"
|
||||
exit 1
|
||||
}
|
||||
[[ "${actual_public_key}" == "${OCR_RUNTIME_INDEX_PUBLIC_KEY_PEM_B64}" ]] || {
|
||||
echo "::error::Verification image OCR public key does not match the release identity"
|
||||
[[ "${actual_public_key}" == "${expected_public_key}" ]] || {
|
||||
echo "::error::Baked OCR trust public key does not match the release identity"
|
||||
exit 1
|
||||
}
|
||||
[[ "${actual_official_container}" == "1" ]] || {
|
||||
|
||||
@@ -203,8 +203,14 @@ describe("OCR v3 bundle release workflow", () => {
|
||||
expect(verifyJob).toContain("timeout-minutes: 90");
|
||||
expect(verifyJob).toContain("docker/verify-ocr-runtime.sh");
|
||||
expect(verifyJob).toContain("Verify image has the release trust identity");
|
||||
expect(verifyJob).toContain("Verification image OCR key ID does not match");
|
||||
expect(verifyJob).toContain("Verification image OCR public key does not match");
|
||||
// The identity is verified against the baked trust FILE, which the runtime
|
||||
// reads, not the OCR_RUNTIME_INDEX_* env vars, which the official image leaves
|
||||
// unset by design. Reading the env compared "" and failed a correct image.
|
||||
expect(verifyJob).toContain("cat /app/docker/ocr-runtime-trust.json");
|
||||
expect(verifyJob).toContain(".keys[0].keyId");
|
||||
expect(verifyJob).toContain("Baked OCR trust key ID");
|
||||
expect(verifyJob).toContain("Baked OCR trust public key does not match");
|
||||
expect(verifyJob).not.toContain('printf %s "$OCR_RUNTIME_INDEX_KEY_ID"');
|
||||
expect(verifyJob).toContain(
|
||||
'actual_official_container="$(docker run --rm --entrypoint sh "${SNAPOTTER_BUNDLE_IMAGE}"',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user