ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
hil_convergence_safety_image_lock_fixtures.py
Go to the documentation of this file.
1# SPDX-License-Identifier: MIT
2# Copyright (c) 2026 Brighton Sikarskie
3"""Static image-lock mutations split from the aggregate convergence fixtures."""
4
5from __future__ import annotations
6
7from hil_convergence_safety_semantic_mutations import process_authority_mutations
8
9Mutation = tuple[str, str, str, str]
10
11
12def _authority_mutations() -> tuple[Mutation, ...]:
13 """Return mutations of the duplicated canonical lock authority."""
14 return (
15 (
16 "managed image lock default moved back into the sticky cache",
17 "dev_defaults",
18 "dev_box_image_lock_dir: /var/cache/ra8-devcontainer-image-lock",
19 "dev_box_image_lock_dir: /var/cache/ra8-tools",
20 ),
21 (
22 "script canonical image lock authority drifted",
23 "devcontainer_image",
24 'CANONICAL_IMAGE_LOCK_DIR="/var/cache/ra8-devcontainer-image-lock"',
25 'CANONICAL_IMAGE_LOCK_DIR="/var/cache/ra8-tools"',
26 ),
27 (
28 "shell profile reintroduces a second image lock authority",
29 "dev_main",
30 ' export RA8_CONTAINER_RUNTIME="{{ dev_box_container_runtime }}"\n',
31 ' export RA8_CONTAINER_RUNTIME="{{ dev_box_container_runtime }}"\n'
32 ' export RA8_IMAGE_LOCK_DIR="{{ dev_box_image_lock_dir }}"\n',
33 ),
34 (
35 "managed image lock transaction include drifted",
36 "dev_transaction",
37 "- name: Converge the managed devcontainer image lock authority\n"
38 " ansible.builtin.include_tasks: image_lock.yml\n",
39 "- name: Converge the managed devcontainer image lock authority\n"
40 " ansible.builtin.include_tasks: hil_runner.yml\n",
41 ),
42 )
43
44
45def _ansible_proof_mutations() -> tuple[Mutation, ...]:
46 """Return Ansible post-stat and identity-proof mutations."""
47 return (
48 *_ansible_stat_mutations(),
49 *_ansible_identity_mutations(),
50 )
51
52
53def _ansible_stat_mutations() -> tuple[Mutation, ...]:
54 """Return Ansible no-follow stat mutations."""
55 return (
56 (
57 "managed image lock marker pre-stat follows links",
58 "dev_main",
59 "- name: Inspect the managed image lock group marker without following links\n"
60 " become: true\n"
61 " ansible.builtin.stat:\n"
62 ' path: "{{ dev_box_image_lock_dir }}/devcontainer-image.gid"\n'
63 " follow: false",
64 "- name: Inspect the managed image lock group marker without following links\n"
65 " become: true\n"
66 " ansible.builtin.stat:\n"
67 ' path: "{{ dev_box_image_lock_dir }}/devcontainer-image.gid"\n'
68 " follow: true",
69 ),
70 (
71 "managed image lock directory post-stat follows links",
72 "dev_main",
73 "- name: Reinspect the converged managed image lock directory\n"
74 " become: true\n"
75 " ansible.builtin.stat:\n"
76 ' path: "{{ dev_box_image_lock_dir }}"\n'
77 " follow: false",
78 "- name: Reinspect the converged managed image lock directory\n"
79 " become: true\n"
80 " ansible.builtin.stat:\n"
81 ' path: "{{ dev_box_image_lock_dir }}"\n'
82 " follow: true",
83 ),
84 (
85 "managed image lock file post-stat follows links",
86 "dev_main",
87 "- name: Reinspect the converged managed image lock\n"
88 " become: true\n"
89 " ansible.builtin.stat:\n"
90 ' path: "{{ dev_box_image_lock_dir }}/devcontainer-image.lock"\n'
91 " follow: false",
92 "- name: Reinspect the converged managed image lock\n"
93 " become: true\n"
94 " ansible.builtin.stat:\n"
95 ' path: "{{ dev_box_image_lock_dir }}/devcontainer-image.lock"\n'
96 " follow: true",
97 ),
98 )
99
100
101def _ansible_identity_mutations() -> tuple[Mutation, ...]:
102 """Return Ansible numeric identity-proof mutations."""
103 return (
104 (
105 "managed image lock directory numeric owner proof weakened",
106 "dev_main",
107 " - dev_box_image_lock_dir_after.stat.uid == 0",
108 " - dev_box_image_lock_dir_after.stat.uid >= 0",
109 ),
110 (
111 "managed image lock numeric owner proof weakened",
112 "dev_main",
113 " - dev_box_image_lock_after.stat.uid == 0",
114 " - dev_box_image_lock_after.stat.uid >= 0",
115 ),
116 (
117 "managed image lock marker post-stat follows links",
118 "dev_main",
119 "- name: Reinspect the converged managed image lock group marker\n"
120 " become: true\n"
121 " ansible.builtin.stat:\n"
122 ' path: "{{ dev_box_image_lock_dir }}/devcontainer-image.gid"\n'
123 " follow: false",
124 "- name: Reinspect the converged managed image lock group marker\n"
125 " become: true\n"
126 " ansible.builtin.stat:\n"
127 ' path: "{{ dev_box_image_lock_dir }}/devcontainer-image.gid"\n'
128 " follow: true",
129 ),
130 (
131 "managed image lock marker numeric owner proof weakened",
132 "dev_main",
133 " - dev_box_image_lock_gid_marker_after.stat.uid == 0",
134 " - dev_box_image_lock_gid_marker_after.stat.uid >= 0",
135 ),
136 (
137 "managed image lock marker numeric group content proof removed",
138 "dev_main",
139 " - dev_box_image_lock_dir_after.stat.gid == "
140 "(dev_box_image_lock_gid.stdout | int)\n"
141 " - dev_box_image_lock_after.stat.gid == "
142 "(dev_box_image_lock_gid.stdout | int)",
143 " - dev_box_image_lock_dir_after.stat.gid == "
144 "(dev_box_image_lock_gid.stdout | int)\n"
145 " - dev_box_image_lock_after.stat.gid >= 0",
146 ),
147 )
148
149
150def _serialization_mutations() -> tuple[Mutation, ...]:
151 """Return no-create, inode, force, and selftest mutations."""
152 return (
153 (
154 "managed image lock open can recreate a missing file",
155 "devcontainer_image",
156 ' exec 9<"$IMAGE_LOCK_FILE" || die "cannot open existing image lock: '
157 '$IMAGE_LOCK_FILE"',
158 ' exec 9>>"$IMAGE_LOCK_FILE" || die "cannot open existing image lock: '
159 '$IMAGE_LOCK_FILE"',
160 ),
161 (
162 "managed image lock pre-flock opened-inode validation removed",
163 "devcontainer_image",
164 " validate_opened_image_lock 9\n if ! flock -n 9; then",
165 " # validate_opened_image_lock 9\n if ! flock -n 9; then",
166 ),
167 (
168 "managed image lock post-flock opened-inode validation removed",
169 "devcontainer_image",
170 " flock 9\n fi\n validate_opened_image_lock 9",
171 " flock 9\n fi\n # validate_opened_image_lock 9",
172 ),
173 (
174 "forced image rebuild bypasses serialization",
175 "devcontainer_image",
176 ' build_locked "$want" forced "" 1',
177 ' build_image "$want"',
178 ),
179 (
180 "managed image lock strict flock refusal bypassed",
181 "devcontainer_image",
182 ' [[ "$IMAGE_LOCK_MANAGED" == "0" ]] ||',
183 ' true || [[ "$IMAGE_LOCK_MANAGED" == "0" ]] ||',
184 ),
185 (
186 "managed image lock selftest dispatcher stranded",
187 "devcontainer_image_selftest_cases",
188 ' dispatch_image_lock_selftest suite "$tmp"',
189 ' # dispatch_image_lock_selftest suite "$tmp"',
190 ),
191 (
192 "managed image lock top-level completion check removed",
193 "devcontainer_image",
194 ' main "$@"\n'
195 ' if [[ "${1:-}" == "--selftest" || "${1:-}" == "--selftest-offline" ]]; then\n'
196 ' [[ "$SELFTEST_MAIN_COMPLETE" == "1" ]] || '
197 'die "selftest main returned before completion"\n'
198 " fi",
199 ' main "$@"',
200 ),
201 (
202 "managed image lock ensure preflight stranded",
203 "devcontainer_image",
204 " managed_image_lock_preflight\n want=",
205 " # managed_image_lock_preflight\n want=",
206 ),
207 )
208
209
210def _selftest_fallback_mutations() -> tuple[Mutation, ...]:
211 """Return controller fallback and escalation mutations."""
212 return (
213 (
214 "image lock signal fallback removed",
215 "devcontainer_image_signal_selftest",
216 ' force_signal_controller_cleanup "$controller" "$case_dir" "$managed" ||\n'
217 ' die "selftest: handler-hang group fallback failed"',
218 " true",
219 ),
220 (
221 "image lock fallback controller TERM removed",
222 "devcontainer_image_lock_selftest",
223 ' signal_owned_controller_group TERM "$controller" 2>/dev/null || return 1',
224 " true",
225 ),
226 (
227 "image lock controller cleanup bound shortened below worker cleanup",
228 "devcontainer_image_lock_selftest",
229 "SELFTEST_CONTROLLER_CLEANUP_STEPS=1600",
230 "SELFTEST_CONTROLLER_CLEANUP_STEPS=50",
231 ),
232 (
233 "image lock unready-controller fallback removed",
234 "devcontainer_image_signal_selftest",
235 ' force_signal_controller_cleanup "$controller" "$case_dir" "$managed" ||\n'
236 ' die "selftest: $signal unready-controller cleanup failed"',
237 " true",
238 ),
239 )
240
241
242def _early_return_mutations(key: str, names: tuple[str, ...], indent: str) -> tuple[Mutation, ...]:
243 """Build early-success mutations for one exact shell authority."""
244 return tuple(
245 (
246 f"image lock {name} early success refused",
247 key,
248 f"{indent}{name}() {{\n",
249 f"{indent}{name}() {{\n{indent} return 0\n",
250 )
251 for name in names
252 )
253
254
255def _receipt_early_return_mutations() -> tuple[Mutation, ...]:
256 """Return must-fire early-success mutations for every receipt boundary."""
257 receipt_names = (
258 "expected_image_lock_suite_receipts",
259 "scenario_receipt_value",
260 "validate_scenario_receipt_directory",
261 "write_scenario_receipt",
262 "require_scenario_receipt",
263 "verify_scenario_receipt_files",
264 "expected_cleanup_receipt",
265 "expected_force_cleanup_receipt",
266 "parent_lock_fd_is_closed",
267 "require_cleanup_receipt",
268 "require_force_cleanup_receipt",
269 "write_worker_cleanup_proof_file",
270 "require_worker_cleanup_proof_file",
271 "write_controller_cleanup_receipt_file",
272 "require_controller_cleanup_receipt_file",
273 )
274 helper_names = (
275 "reap_worker",
276 "reap_controller",
277 "release_parent_lock",
278 "fresh_lock_probe",
279 "assert_no_surviving_descendants",
280 "cleanup_image_lock_case",
281 "force_signal_controller_cleanup",
282 "verify_signal_controller_cleanup",
283 "verify_image_lock_suite_receipts",
284 "run_image_lock_scenario",
285 "dispatch_image_lock_selftest",
286 "selftest_early_exit",
287 "selftest_pre_ready_hang",
288 "selftest_forced_build_contention",
289 "selftest_post_ready_hang",
290 )
291 return (
292 *_early_return_mutations("devcontainer_image", ("build_locked", "main"), " "),
293 *_early_return_mutations("devcontainer_image_lock_receipts", receipt_names, ""),
294 *_early_return_mutations("devcontainer_image_lock_selftest", helper_names, ""),
295 *_early_return_mutations("devcontainer_image_selftest_cases", ("cmd_selftest",), ""),
296 *_early_return_mutations(
297 "devcontainer_image_signal_selftest",
298 ("selftest_signal_ready_timeout", "selftest_signal_cleanup"),
299 "",
300 ),
301 )
302
303
304def _receipt_semantic_mutations() -> tuple[Mutation, ...]:
305 """Return removals of the scenario facts that authorize final receipts."""
306 post_ready_negative = (
307 ' if wait_for_status_file "$SELFTEST_CASE_DIR/done.status" '
308 '"$SELFTEST_WORKER_PID"; then\n'
309 ' die "selftest: build-hang worker unexpectedly completed"\n'
310 " fi\n"
311 )
312 ready_timeout_negative = (
313 ' if wait_for_status_file "$case_dir/controller-ready.status" "$controller"; then\n'
314 ' die "selftest: delayed signal controller unexpectedly became ready"\n'
315 " fi\n"
316 )
317 contention_loop = (
318 " for ((attempt = 0; attempt < 20; ++attempt)); do\n"
319 ' [[ ! -e "$SELFTEST_CASE_DIR/build-entered.status" ]] ||\n'
320 ' die "selftest: forced rebuild bypassed the held lock"\n'
321 ' process_is_terminal "$SELFTEST_WORKER_PID" &&\n'
322 ' die "selftest: forced rebuild exited while the lock was held"\n'
323 " sleep 0.01\n"
324 " done\n"
325 )
326 signal_status = (
327 ' [[ "$SELFTEST_REAP_STATUS" == "$expected" ]] ||\n'
328 ' die "selftest: $signal controller returned $SELFTEST_REAP_STATUS, '
329 'expected $expected"\n'
330 )
331 return (
332 (
333 "image lock post-ready negative completion proof removed",
334 "devcontainer_image_lock_selftest",
335 post_ready_negative,
336 "",
337 ),
338 (
339 "image lock post-ready proof stranded in comments",
340 "devcontainer_image_lock_selftest",
341 post_ready_negative,
342 " # post-ready negative completion proof removed\n",
343 ),
344 (
345 "image lock signal-ready negative deadline proof removed",
346 "devcontainer_image_signal_selftest",
347 ready_timeout_negative,
348 "",
349 ),
350 (
351 "image lock signal expected status proof removed",
352 "devcontainer_image_signal_selftest",
353 signal_status,
354 "",
355 ),
356 (
357 "image lock forced contention observation loop removed",
358 "devcontainer_image_lock_selftest",
359 contention_loop,
360 "",
361 ),
362 )
363
364
365def _raw_return_mutations() -> tuple[Mutation, ...]:
366 """Return equivalent Bash success spellings rejected by the raw digest."""
367 opening = "parent_lock_fd_is_closed() {\n"
368 variants = (
369 ("return plus-zero", " return +0\n"),
370 ("return double-zero", " return 00\n"),
371 ("arithmetic return", ' return "$((0))"\n'),
372 ("command return", " command return 0\n"),
373 ("builtin return", " builtin return 0\n"),
374 ("exit success", " exit 0\n"),
375 ("inline success", " true; return 0\n"),
376 )
377 return tuple(
378 (
379 f"image lock raw digest rejects {label}",
380 "devcontainer_image_lock_receipts",
381 opening,
382 opening + payload,
383 )
384 for label, payload in variants
385 )
386
387
388def _raw_surface_mutations() -> tuple[Mutation, ...]:
389 """Return raw comment and alternate-definition mutations."""
390 return (
391 (
392 "image lock entry raw-byte comment change",
393 "devcontainer_image",
394 "# Copyright (c) 2026 Brighton Sikarskie\n",
395 "# Copyright (c) 2026 Brighton Sikarskie \n",
396 ),
397 (
398 "image lock helper raw-byte comment change",
399 "devcontainer_image_lock_selftest",
400 "# Copyright (c) 2026 Brighton Sikarskie\n",
401 "# Copyright (c) 2026 Brighton Sikarskie \n",
402 ),
403 (
404 "image lock receipt helper raw-byte comment change",
405 "devcontainer_image_lock_receipts",
406 "# Copyright (c) 2026 Brighton Sikarskie\n",
407 "# Copyright (c) 2026 Brighton Sikarskie \n",
408 ),
409 (
410 "image lock appended alternate function definition",
411 "devcontainer_image_lock_selftest",
412 " *) die \"unknown image-lock selftest dispatch '$command'\" ;;\n esac\n}\n",
413 " *) die \"unknown image-lock selftest dispatch '$command'\" ;;\n"
414 " esac\n"
415 "}\n\n"
416 "parent_lock_fd_is_closed() {\n"
417 " return +0\n"
418 "}\n",
419 ),
420 )
421
422
423def _raw_digest_mutations() -> tuple[Mutation, ...]:
424 """Return raw byte, alternate-definition, and stranded-proof attacks."""
425 return (
426 *_raw_return_mutations(),
427 *_raw_surface_mutations(),
428 (
429 "image lock active cleanup proof replaced by no-op",
430 "devcontainer_image_lock_selftest",
431 " parent_lock_fd_is_closed || cleanup_failed=1\n",
432 " true\n",
433 ),
434 (
435 "image lock cleanup proof stranded in a string",
436 "devcontainer_image_lock_selftest",
437 " parent_lock_fd_is_closed || cleanup_failed=1\n",
438 " printf '%s\\n' 'parent_lock_fd_is_closed' >/dev/null\n",
439 ),
440 (
441 "image lock cleanup proof stranded in a comment",
442 "devcontainer_image_lock_selftest",
443 " parent_lock_fd_is_closed || cleanup_failed=1\n",
444 " # parent_lock_fd_is_closed || cleanup_failed=1\n",
445 ),
446 (
447 "image lock signal-controller sleep inherits parent lock FD",
448 "devcontainer_image_lock_selftest",
449 ' if [[ "$readiness_mode" == "delay-controller-ready" ]]; then\n'
450 " while :; do sleep 1 8>&-; done\n"
451 " fi\n",
452 ' if [[ "$readiness_mode" == "delay-controller-ready" ]]; then\n'
453 " while :; do sleep 1; done\n"
454 " fi\n",
455 ),
456 )
457
458
459def _selftest_semantic_attack_mutations() -> tuple[Mutation, ...]:
460 """Return managed-object semantic attack mutations."""
461 return (
462 (
463 "missing managed group marker attack removed",
464 "devcontainer_image_lock_selftest",
465 'if (RA8_IMAGE_LOCK_DIR="$tmp/missing-marker" resolve_image_lock ',
466 "if (false && RA8_IMAGE_LOCK_DIR=unused resolve_image_lock ",
467 ),
468 (
469 "symlinked managed group marker attack removed",
470 "devcontainer_image_lock_selftest",
471 ' die "selftest: symlinked managed image lock group marker passed"',
472 " true",
473 ),
474 (
475 "hardlinked managed group marker attack removed",
476 "devcontainer_image_lock_selftest",
477 ' die "selftest: multiply-linked managed image lock group marker passed"',
478 " true",
479 ),
480 (
481 "wrong managed directory group attack removed",
482 "devcontainer_image_lock_selftest",
483 ' die "selftest: wrong managed image lock directory group passed"',
484 " true",
485 ),
486 (
487 "wrong managed lock group attack removed",
488 "devcontainer_image_lock_selftest",
489 ' die "selftest: wrong managed image lock file group passed"',
490 " true",
491 ),
492 (
493 "wrong managed group marker owner attack removed",
494 "devcontainer_image_lock_selftest",
495 ' die "selftest: non-root managed image lock group marker passed"',
496 " true",
497 ),
498 (
499 "wrong managed group marker mode attack removed",
500 "devcontainer_image_lock_selftest",
501 ' die "selftest: writable managed image lock group marker passed"',
502 " true",
503 ),
504 (
505 "wrong managed group marker content attack removed",
506 "devcontainer_image_lock_selftest",
507 ' die "selftest: root group in managed image lock group marker passed"',
508 " true",
509 ),
510 (
511 "binary managed group marker attack removed",
512 "devcontainer_image_lock_selftest",
513 ' die "selftest: binary managed image lock group marker passed"',
514 " true",
515 ),
516 )
517
518
519def _extraction_authority_mutations() -> tuple[Mutation, ...]:
520 """Return load-bearing mutations for extracted supervisor responsibilities."""
521 return (
522 (
523 "supervisor interruption-handler definition removed",
524 "devcontainer_image_selftest_supervisor",
525 "def _install_interruption_handlers(supervisor: BoundGroup) -> None:",
526 "def _install_interruption_handlers_disabled(supervisor: BoundGroup) -> None:",
527 ),
528 (
529 "supervisor interruption-handler call removed",
530 "devcontainer_image_selftest_supervisor",
531 "_install_interruption_handlers(supervisor)",
532 "pass # mutation: handler install removed",
533 ),
534 (
535 "closed-controller command definition removed",
536 "devcontainer_image_selftest_supervisor_cases",
537 "def _closed_controller_command(",
538 "def _closed_controller_command_disabled(",
539 ),
540 (
541 "closed-controller command call removed",
542 "devcontainer_image_selftest_supervisor_cases",
543 "command = _closed_controller_command(\n"
544 " entry_authority, status, death_descriptor, root_descriptor, "
545 "root_identity\n"
546 " )\n"
547 " child = subprocess.Popen(",
548 "child = subprocess.Popen(",
549 ),
550 )
551
552
553def runtime_selftest_tail_mutations() -> tuple[Mutation, ...]:
554 """Return process-controller fallback and semantic attack mutations."""
555 return (
556 *_selftest_fallback_mutations(),
557 *process_authority_mutations(),
558 *_extraction_authority_mutations(),
559 *_selftest_semantic_attack_mutations(),
560 )
561
562
563def runtime_readiness_mutations() -> tuple[Mutation, ...]:
564 """Return readiness, liveness, and cleanup-trap mutations."""
565 return (
566 (
567 "forced-build readiness deadline removed",
568 "devcontainer_image_lock_selftest",
569 "wait_for_status_file() {\n"
570 ' local path="$1" pid="$2" attempt\n'
571 " for ((attempt = 0; attempt < SELFTEST_DEADLINE_STEPS; ++attempt)); do",
572 'wait_for_status_file() {\n local path="$1" pid="$2" attempt\n while true; do',
573 ),
574 (
575 "forced-build readiness child-liveness check bypassed",
576 "devcontainer_image_lock_selftest",
577 ' if process_is_terminal "$pid"; then\n'
578 ' [[ -s "$path" ]] && return 0\n'
579 " return 1",
580 " if false; then\n return 0\n return 1",
581 ),
582 (
583 "forced-build failure cleanup trap removed",
584 "devcontainer_image_lock_selftest",
585 " trap image_lock_case_exit EXIT",
586 " trap ':' EXIT",
587 ),
588 )
589
590
591def runtime_worker_mutations() -> tuple[Mutation, ...]:
592 """Return isolated worker-group handshake mutations."""
593 return (
594 (
595 "image lock worker process-group isolation removed",
596 "devcontainer_image_lock_selftest",
597 ' exec /usr/bin/setsid /bin/bash -p -- "$SELFTEST_IMAGE_ENTRY"',
598 ' exec /bin/bash -p -- "$SELFTEST_IMAGE_ENTRY"',
599 ),
600 (
601 "image lock worker inherited parent lock close removed",
602 "devcontainer_image_lock_selftest",
603 " exec 8>&-\n trap '' HUP INT TERM\n"
604 ' exec /usr/bin/setsid /bin/bash -p -- "$SELFTEST_IMAGE_ENTRY"',
605 " :\n trap '' HUP INT TERM\n"
606 ' exec /usr/bin/setsid /bin/bash -p -- "$SELFTEST_IMAGE_ENTRY"',
607 ),
608 (
609 "image lock worker verified group handshake removed",
610 "devcontainer_image_lock_selftest",
611 " if ! read_worker_group; then\n abort_bound_worker_spawn 1\n fi",
612 " if false; then\n abort_bound_worker_spawn 1\n fi",
613 ),
614 (
615 "image lock worker group record moved after dispatch",
616 "devcontainer_image_lock_selftest",
617 ' record_worker_group "$case_dir" "$mode"\n'
618 ' wait_for_worker_ack "$case_dir" "$$" || exit 124',
619 ' wait_for_worker_ack "$case_dir" "$$" || exit 124\n'
620 ' record_worker_group "$case_dir" "$mode"',
621 ),
622 (
623 "image lock worker pgid equality guard removed",
624 "devcontainer_image_lock_selftest",
625 ' [[ "$pgid" == "$pid" ]] || die "selftest isolated worker is not its group leader"',
626 " true",
627 ),
628 (
629 "image lock worker own-group guard removed",
630 "devcontainer_image_lock_selftest",
631 ' [[ "$SELFTEST_WORKER_PGID" != "$own_pgid" ]]',
632 " true",
633 ),
634 )
635
636
637def runtime_loader_mutations() -> tuple[Mutation, ...]:
638 """Return authenticated receipt and lock helper loader mutations."""
639 return (
640 (
641 "managed image lock receipt helper source removed",
642 "devcontainer_image_selftest_cases",
643 ' source_approved_selftest_helper "$receipts" "$IMAGE_LOCK_RECEIPTS_RAW_SHA256"',
644 " true",
645 ),
646 (
647 "managed image lock receipt source-only diagnostic weakened",
648 "devcontainer_image_selftest_cases",
649 ' "$output" == "error: devcontainer image lock receipt helper is source-only"',
650 ' "$output" != ""',
651 ),
652 (
653 "managed image lock selftest helper source removed",
654 "devcontainer_image_selftest_cases",
655 ' source_approved_selftest_helper "$helper" "$IMAGE_LOCK_SELFTEST_RAW_SHA256"',
656 " true",
657 ),
658 (
659 "managed image lock receipt helper loaded after its consumer",
660 "devcontainer_image_selftest_cases",
661 ' source_approved_selftest_helper "$receipts" "$IMAGE_LOCK_RECEIPTS_RAW_SHA256"\n'
662 ' source_approved_selftest_helper "$helper" "$IMAGE_LOCK_SELFTEST_RAW_SHA256"',
663 ' source_approved_selftest_helper "$helper" "$IMAGE_LOCK_SELFTEST_RAW_SHA256"\n'
664 ' source_approved_selftest_helper "$receipts" "$IMAGE_LOCK_RECEIPTS_RAW_SHA256"',
665 ),
666 )
667
668
669def mutations() -> tuple[Mutation, ...]:
670 """Return the split image-lock authority/proof/serialization mutations."""
671 return (
672 *_authority_mutations(),
673 *_ansible_proof_mutations(),
674 *_serialization_mutations(),
675 *_receipt_early_return_mutations(),
676 *_receipt_semantic_mutations(),
677 *_raw_digest_mutations(),
678 )