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: |
|
run: |
|
||||||
: "${OCR_RUNTIME_INDEX_KEY_ID:?Set repository variable OCR_RUNTIME_INDEX_KEY_ID}"
|
: "${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}"
|
: "${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}" \
|
# The official image bakes the trust store as a FILE
|
||||||
-c 'printf %s "$OCR_RUNTIME_INDEX_KEY_ID"')"
|
# (/app/docker/ocr-runtime-trust.json, written by write-ocr-runtime-trust.mjs)
|
||||||
actual_public_key="$(docker run --rm --entrypoint sh "${SNAPOTTER_BUNDLE_IMAGE}" \
|
# and deliberately leaves OCR_RUNTIME_INDEX_* unset in the image env: those
|
||||||
-c 'printf %s "$OCR_RUNTIME_INDEX_PUBLIC_KEY_PEM_B64"')"
|
# 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}" \
|
actual_official_container="$(docker run --rm --entrypoint sh "${SNAPOTTER_BUNDLE_IMAGE}" \
|
||||||
-c 'printf %s "$SNAPOTTER_OFFICIAL_CONTAINER"')"
|
-c 'printf %s "$SNAPOTTER_OFFICIAL_CONTAINER"')"
|
||||||
[[ "${actual_key_id}" == "${OCR_RUNTIME_INDEX_KEY_ID}" ]] || {
|
[[ "${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
|
exit 1
|
||||||
}
|
}
|
||||||
[[ "${actual_public_key}" == "${OCR_RUNTIME_INDEX_PUBLIC_KEY_PEM_B64}" ]] || {
|
[[ "${actual_public_key}" == "${expected_public_key}" ]] || {
|
||||||
echo "::error::Verification image OCR public key does not match the release identity"
|
echo "::error::Baked OCR trust public key does not match the release identity"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
[[ "${actual_official_container}" == "1" ]] || {
|
[[ "${actual_official_container}" == "1" ]] || {
|
||||||
|
|||||||
@@ -203,8 +203,14 @@ describe("OCR v3 bundle release workflow", () => {
|
|||||||
expect(verifyJob).toContain("timeout-minutes: 90");
|
expect(verifyJob).toContain("timeout-minutes: 90");
|
||||||
expect(verifyJob).toContain("docker/verify-ocr-runtime.sh");
|
expect(verifyJob).toContain("docker/verify-ocr-runtime.sh");
|
||||||
expect(verifyJob).toContain("Verify image has the release trust identity");
|
expect(verifyJob).toContain("Verify image has the release trust identity");
|
||||||
expect(verifyJob).toContain("Verification image OCR key ID does not match");
|
// The identity is verified against the baked trust FILE, which the runtime
|
||||||
expect(verifyJob).toContain("Verification image OCR public key does not match");
|
// 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(
|
expect(verifyJob).toContain(
|
||||||
'actual_official_container="$(docker run --rm --entrypoint sh "${SNAPOTTER_BUNDLE_IMAGE}"',
|
'actual_official_container="$(docker run --rm --entrypoint sh "${SNAPOTTER_BUNDLE_IMAGE}"',
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user