3"""Typed authority for every first-party shell entry point.
5This table is intentionally exhaustive. The shebang gate compares it with the
6repository's canonical shell census, so a new shell file cannot inherit any
7policy by naming convention. Startup security, entry/source usage, dialect,
8and executable mode are independent axes. Security-pinned entries use the
9canonical combined preamble: shebang, SPDX, copyright, then the exact
10``SHEBANG-SECURITY`` rationale. A privileged sourced-only helper,
11for example, must keep its protected header but can only be sourced by a
12privileged parent; it never becomes a launchable privileged entry merely
13because it uses the protected header.
16from __future__
import annotations
18from dataclasses
import dataclass
19from enum
import StrEnum
21from shell_entrypoint_policy_ci
import CI_POLICY_ROWS
22from shell_entrypoint_policy_hil
import HIL_POLICY_ROWS, ShellPolicyRow
25class ShellSecurity(StrEnum):
26 """Startup trust required by a shell file."""
29 PRIVILEGED =
"privileged"
32class ShellUsage(StrEnum):
33 """Whether a file is an entry point, a sourced helper, or both."""
36 SOURCED_ONLY =
"sourced-only"
40class ShellDialect(StrEnum):
41 """Exact interpreter dialect for portable headers."""
47class DuplicateShellPolicyError(ValueError):
48 """A path appeared in more than one typed policy domain."""
51@dataclass(frozen=
True)
53 """One shell path's independent startup, usage, dialect, and mode contract."""
55 security: ShellSecurity
59 source_requires_privileged_parent: bool
62PRIVILEGED_SHEBANG =
"#!/bin/bash -p"
64 "# SHEBANG-SECURITY: -p blocks BASH_ENV and exported-function startup injection."
66PORTABLE_SHEBANG =
"#!/usr/bin/env bash"
67PORTABLE_SH_SHEBANG =
"#!/usr/bin/env sh"
69_BASE_SHELL_POLICIES: dict[str, ShellPolicy] = {
70 "apps/host/mdl/tests/http_integration.sh": ShellPolicy(
71 ShellSecurity.PORTABLE,
75 source_requires_privileged_parent=
False,
77 "apps/host/mdl/tests/integration.sh": ShellPolicy(
78 ShellSecurity.PORTABLE,
82 source_requires_privileged_parent=
False,
84 "coprocessor/esp32c6/build.sh": ShellPolicy(
85 ShellSecurity.PRIVILEGED,
89 source_requires_privileged_parent=
False,
91 "coprocessor/esp32c6/flash.sh": ShellPolicy(
92 ShellSecurity.PRIVILEGED,
96 source_requires_privileged_parent=
False,
98 "examples/ek_ra8d2/hw_pending/ereader_m33/tests/scripts/emu_handoff_gate.sh": ShellPolicy(
99 ShellSecurity.PORTABLE,
103 source_requires_privileged_parent=
False,
105 "examples/ek_ra8d2/hw_pending/ereader_m33/tests/scripts/emu_render_gate.sh": ShellPolicy(
106 ShellSecurity.PORTABLE,
110 source_requires_privileged_parent=
False,
112 "examples/ek_ra8d2/hw_validated/hil/dfu_copy_to_run/scripts/build_payload.sh": ShellPolicy(
113 ShellSecurity.PORTABLE,
117 source_requires_privileged_parent=
False,
120 "examples/ek_ra8d2/hw_validated/hil/glcdc_render/tests/scripts/ra8_emulator_fb_crc.sh"
122 ShellSecurity.PORTABLE,
126 source_requires_privileged_parent=
False,
128 "infra/bootstrap.sh": ShellPolicy(
129 ShellSecurity.PRIVILEGED,
133 source_requires_privileged_parent=
False,
135 "infra/network/ap_openwrt.sh": ShellPolicy(
136 ShellSecurity.PRIVILEGED,
140 source_requires_privileged_parent=
False,
142 "infra/network/verify_bench_wifi.sh": ShellPolicy(
143 ShellSecurity.PRIVILEGED,
147 source_requires_privileged_parent=
False,
149 "scripts/builders/all_examples.sh": ShellPolicy(
150 ShellSecurity.PORTABLE,
154 source_requires_privileged_parent=
False,
156 "scripts/builders/books.sh": ShellPolicy(
157 ShellSecurity.PORTABLE,
161 source_requires_privileged_parent=
False,
163 "scripts/builders/build_host_tools.sh": ShellPolicy(
164 ShellSecurity.PORTABLE,
168 source_requires_privileged_parent=
False,
170 "scripts/builders/build_shared_libs.sh": ShellPolicy(
171 ShellSecurity.PORTABLE,
175 source_requires_privileged_parent=
False,
177 "scripts/builders/docs.sh": ShellPolicy(
178 ShellSecurity.PORTABLE,
182 source_requires_privileged_parent=
False,
184 "scripts/builders/host_cmake.sh": ShellPolicy(
185 ShellSecurity.PORTABLE,
189 source_requires_privileged_parent=
False,
191 "scripts/builders/init_fuzz_corpora.sh": ShellPolicy(
192 ShellSecurity.PORTABLE,
196 source_requires_privileged_parent=
False,
198 "scripts/builders/lib/app_batch.sh": ShellPolicy(
199 ShellSecurity.PORTABLE,
200 ShellUsage.SOURCED_ONLY,
203 source_requires_privileged_parent=
False,
205 "scripts/builders/provision_doxygen.sh": ShellPolicy(
206 ShellSecurity.PRIVILEGED,
210 source_requires_privileged_parent=
False,
212 "scripts/builders/publish_docs.sh": ShellPolicy(
213 ShellSecurity.PRIVILEGED,
217 source_requires_privileged_parent=
False,
219 "scripts/builders/select_host_compiler.sh": ShellPolicy(
220 ShellSecurity.PORTABLE,
221 ShellUsage.SOURCED_ONLY,
224 source_requires_privileged_parent=
False,
226 "scripts/checks/check_nsc_cmse.sh": ShellPolicy(
227 ShellSecurity.PORTABLE,
231 source_requires_privileged_parent=
False,
233 "scripts/checks/check_stack_usage.sh": ShellPolicy(
234 ShellSecurity.PORTABLE,
238 source_requires_privileged_parent=
False,
240 "scripts/checks/check_unicorn_version.sh": ShellPolicy(
241 ShellSecurity.PORTABLE,
245 source_requires_privileged_parent=
False,
247 "scripts/checks/clang_tidy.sh": ShellPolicy(
248 ShellSecurity.PORTABLE,
252 source_requires_privileged_parent=
False,
254 "scripts/checks/cppcheck.sh": ShellPolicy(
255 ShellSecurity.PRIVILEGED,
259 source_requires_privileged_parent=
False,
261 "scripts/checks/format_code.sh": ShellPolicy(
262 ShellSecurity.PORTABLE,
266 source_requires_privileged_parent=
False,
268 "scripts/checks/format_tree.sh": ShellPolicy(
269 ShellSecurity.PORTABLE,
273 source_requires_privileged_parent=
False,
275 "scripts/checks/lint_selftest.sh": ShellPolicy(
276 ShellSecurity.PRIVILEGED,
280 source_requires_privileged_parent=
False,
282 "scripts/checks/misra_check.sh": ShellPolicy(
283 ShellSecurity.PORTABLE,
287 source_requires_privileged_parent=
False,
289 "scripts/checks/misra_check_inner.sh": ShellPolicy(
290 ShellSecurity.PORTABLE,
294 source_requires_privileged_parent=
False,
296 "scripts/checks/misra/selftest.sh": ShellPolicy(
297 ShellSecurity.PORTABLE,
298 ShellUsage.SOURCED_ONLY,
301 source_requires_privileged_parent=
False,
303 "scripts/checks/osv_scan.sh": ShellPolicy(
304 ShellSecurity.PORTABLE,
308 source_requires_privileged_parent=
False,
310 "scripts/checks/run_fuzz.sh": ShellPolicy(
311 ShellSecurity.PORTABLE,
315 source_requires_privileged_parent=
False,
317 "scripts/checks/scan_build.sh": ShellPolicy(
318 ShellSecurity.PORTABLE,
322 source_requires_privileged_parent=
False,
324 "scripts/checks/tidy/collect.sh": ShellPolicy(
325 ShellSecurity.PORTABLE,
326 ShellUsage.SOURCED_ONLY,
329 source_requires_privileged_parent=
False,
331 "scripts/checks/tidy/compile_db.sh": ShellPolicy(
332 ShellSecurity.PORTABLE,
333 ShellUsage.SOURCED_ONLY,
336 source_requires_privileged_parent=
False,
338 "scripts/checks/tidy/invoke.sh": ShellPolicy(
339 ShellSecurity.PORTABLE,
340 ShellUsage.SOURCED_ONLY,
343 source_requires_privileged_parent=
False,
345 "scripts/checks/tidy/pass_args.sh": ShellPolicy(
346 ShellSecurity.PORTABLE,
347 ShellUsage.SOURCED_ONLY,
350 source_requires_privileged_parent=
False,
352 "scripts/checks/tidy/passes.sh": ShellPolicy(
353 ShellSecurity.PORTABLE,
354 ShellUsage.SOURCED_ONLY,
357 source_requires_privileged_parent=
False,
359 "scripts/checks/tidy/selftest.sh": ShellPolicy(
360 ShellSecurity.PORTABLE,
361 ShellUsage.SOURCED_ONLY,
364 source_requires_privileged_parent=
False,
366 "scripts/dev/agent_workspace.sh": ShellPolicy(
367 ShellSecurity.PRIVILEGED,
371 source_requires_privileged_parent=
False,
373 "scripts/dev/agent_workspace_selftest.sh": ShellPolicy(
374 ShellSecurity.PORTABLE,
378 source_requires_privileged_parent=
False,
380 "scripts/dev/debug.sh": ShellPolicy(
381 ShellSecurity.PRIVILEGED,
385 source_requires_privileged_parent=
False,
387 "scripts/dev/exfat_macos_interop.sh": ShellPolicy(
388 ShellSecurity.PORTABLE,
392 source_requires_privileged_parent=
False,
394 "scripts/dev/flash.sh": ShellPolicy(
395 ShellSecurity.PRIVILEGED,
399 source_requires_privileged_parent=
False,
401 "scripts/dev/git_environment.sh": ShellPolicy(
402 ShellSecurity.PRIVILEGED,
403 ShellUsage.SOURCED_ONLY,
406 source_requires_privileged_parent=
True,
408 "scripts/dev/hil_cache_repair.sh": ShellPolicy(
409 ShellSecurity.PRIVILEGED,
413 source_requires_privileged_parent=
False,
415 "scripts/dev/infra.sh": ShellPolicy(
416 ShellSecurity.PORTABLE,
420 source_requires_privileged_parent=
False,
422 "scripts/dev/monitor.sh": ShellPolicy(
423 ShellSecurity.PRIVILEGED,
427 source_requires_privileged_parent=
False,
429 "scripts/dev/openocd_debug.sh": ShellPolicy(
430 ShellSecurity.PRIVILEGED,
434 source_requires_privileged_parent=
False,
436 "scripts/dev/openocd_flash.sh": ShellPolicy(
437 ShellSecurity.PRIVILEGED,
441 source_requires_privileged_parent=
False,
443 "scripts/dev/ozone.sh": ShellPolicy(
444 ShellSecurity.PRIVILEGED,
448 source_requires_privileged_parent=
False,
450 "scripts/dev/provision_dev_box_toolchain.sh": ShellPolicy(
451 ShellSecurity.PRIVILEGED,
455 source_requires_privileged_parent=
False,
457 "scripts/dev/provision_dev_box_toolchain_selftest.bash": ShellPolicy(
458 ShellSecurity.PRIVILEGED,
459 ShellUsage.SOURCED_ONLY,
462 source_requires_privileged_parent=
True,
464 "scripts/dev/remote_gdb_server.sh": ShellPolicy(
465 ShellSecurity.PRIVILEGED,
469 source_requires_privileged_parent=
False,
471 "scripts/dev/run_just.sh": ShellPolicy(
472 ShellSecurity.PRIVILEGED,
476 source_requires_privileged_parent=
False,
478 "scripts/dev/setup_ansible.sh": ShellPolicy(
479 ShellSecurity.PRIVILEGED,
483 source_requires_privileged_parent=
False,
485 "scripts/dev/setup_python.sh": ShellPolicy(
486 ShellSecurity.PRIVILEGED,
490 source_requires_privileged_parent=
False,
492 "scripts/emu/eil_all.sh": ShellPolicy(
493 ShellSecurity.PRIVILEGED,
497 source_requires_privileged_parent=
False,
499 "scripts/emu/emu_fixtures.sh": ShellPolicy(
500 ShellSecurity.PORTABLE,
501 ShellUsage.SOURCED_ONLY,
504 source_requires_privileged_parent=
False,
506 "scripts/emu/matrix.sh": ShellPolicy(
507 ShellSecurity.PORTABLE,
511 source_requires_privileged_parent=
False,
513 "scripts/emu/matrix_triage.sh": ShellPolicy(
514 ShellSecurity.PORTABLE,
518 source_requires_privileged_parent=
False,
520 "scripts/emu/setup_macos.sh": ShellPolicy(
521 ShellSecurity.PRIVILEGED,
525 source_requires_privileged_parent=
False,
527 "scripts/emu/smoke.sh": ShellPolicy(
528 ShellSecurity.PORTABLE,
532 source_requires_privileged_parent=
False,
534 "scripts/emu/smoke_apps.sh": ShellPolicy(
535 ShellSecurity.PORTABLE,
536 ShellUsage.SOURCED_ONLY,
539 source_requires_privileged_parent=
False,
541 "scripts/emu/smoke_assert.sh": ShellPolicy(
542 ShellSecurity.PORTABLE,
543 ShellUsage.SOURCED_ONLY,
546 source_requires_privileged_parent=
False,
548 "scripts/emu/smoke_run.sh": ShellPolicy(
549 ShellSecurity.PORTABLE,
550 ShellUsage.SOURCED_ONLY,
553 source_requires_privileged_parent=
False,
555 "scripts/gen/build_chapter_map.sh": ShellPolicy(
556 ShellSecurity.PORTABLE,
560 source_requires_privileged_parent=
False,
562 "scripts/gen/gen_ra8_media_proto.sh": ShellPolicy(
563 ShellSecurity.PORTABLE,
567 source_requires_privileged_parent=
False,
569 "scripts/git/commit-msg": ShellPolicy(
570 ShellSecurity.PRIVILEGED,
574 source_requires_privileged_parent=
False,
576 "scripts/git/github_askpass.sh": ShellPolicy(
577 ShellSecurity.PORTABLE,
581 source_requires_privileged_parent=
False,
583 "scripts/git/hook-launcher": ShellPolicy(
584 ShellSecurity.PRIVILEGED,
588 source_requires_privileged_parent=
False,
590 "scripts/git/install-hooks.sh": ShellPolicy(
591 ShellSecurity.PRIVILEGED,
595 source_requires_privileged_parent=
False,
597 "scripts/git/post-checkout": ShellPolicy(
598 ShellSecurity.PORTABLE,
600 ShellDialect.POSIX_SH,
602 source_requires_privileged_parent=
False,
604 "scripts/git/post-commit": ShellPolicy(
605 ShellSecurity.PORTABLE,
607 ShellDialect.POSIX_SH,
609 source_requires_privileged_parent=
False,
611 "scripts/git/post-merge": ShellPolicy(
612 ShellSecurity.PORTABLE,
614 ShellDialect.POSIX_SH,
616 source_requires_privileged_parent=
False,
618 "scripts/git/pre-commit": ShellPolicy(
619 ShellSecurity.PRIVILEGED,
623 source_requires_privileged_parent=
False,
625 "scripts/git/pre-push": ShellPolicy(
626 ShellSecurity.PRIVILEGED,
630 source_requires_privileged_parent=
False,
632 "scripts/report/mcdc_report.sh": ShellPolicy(
633 ShellSecurity.PORTABLE,
637 source_requires_privileged_parent=
False,
639 "scripts/report/tree_coverage.sh": ShellPolicy(
640 ShellSecurity.PORTABLE,
644 source_requires_privileged_parent=
False,
646 "scripts/secrets/openbao_configure.sh": ShellPolicy(
647 ShellSecurity.PRIVILEGED,
651 source_requires_privileged_parent=
False,
653 "scripts/secrets/openbao_unseal.sh": ShellPolicy(
654 ShellSecurity.PRIVILEGED,
658 source_requires_privileged_parent=
False,
660 "scripts/secrets/rot_provision.sh": ShellPolicy(
661 ShellSecurity.PRIVILEGED,
665 source_requires_privileged_parent=
False,
667 "tests/build_tests.sh": ShellPolicy(
668 ShellSecurity.PORTABLE,
672 source_requires_privileged_parent=
False,
674 "tests/fixtures/epub/run_probe.sh": ShellPolicy(
675 ShellSecurity.PORTABLE,
679 source_requires_privileged_parent=
False,
681 "tests/run_tests.sh": ShellPolicy(
682 ShellSecurity.PORTABLE,
686 source_requires_privileged_parent=
False,
688 "tools/exfat_mkimage/tests/integration.sh": ShellPolicy(
689 ShellSecurity.PORTABLE,
693 source_requires_privileged_parent=
False,
695 "tools/glyph_bench/tests/integration.sh": ShellPolicy(
696 ShellSecurity.PORTABLE,
697 ShellUsage.SOURCED_ONLY,
700 source_requires_privileged_parent=
False,
702 "tools/mkbookimg/tests/integration.sh": ShellPolicy(
703 ShellSecurity.PORTABLE,
707 source_requires_privileged_parent=
False,
709 "tools/mkfontimg/tests/integration.sh": ShellPolicy(
710 ShellSecurity.PORTABLE,
714 source_requires_privileged_parent=
False,
716 "tools/rabook_imagepack/tests/check_production_surface.sh": ShellPolicy(
717 ShellSecurity.PORTABLE,
719 ShellDialect.POSIX_SH,
721 source_requires_privileged_parent=
False,
723 "tools/rabook_imagepack/tests/convert_integration.sh": ShellPolicy(
724 ShellSecurity.PORTABLE,
726 ShellDialect.POSIX_SH,
728 source_requires_privileged_parent=
False,
730 "tools/rabook_imagepack/tests/rabook_inspect_integration.sh": ShellPolicy(
731 ShellSecurity.PORTABLE,
733 ShellDialect.POSIX_SH,
735 source_requires_privileged_parent=
False,
737 "tools/rabook_imagepack/tests/verify_integration.sh": ShellPolicy(
738 ShellSecurity.PORTABLE,
740 ShellDialect.POSIX_SH,
742 source_requires_privileged_parent=
False,
744 "tools/rabook_viewer/tests/run_corpus.sh": ShellPolicy(
745 ShellSecurity.PORTABLE,
749 source_requires_privileged_parent=
False,
751 "tools/rabook_viewer/tests/run_workspace_test.sh": ShellPolicy(
752 ShellSecurity.PORTABLE,
753 ShellUsage.SOURCED_ONLY,
756 source_requires_privileged_parent=
False,
758 "tools/reader_vmem/tests/integration.sh": ShellPolicy(
759 ShellSecurity.PORTABLE,
763 source_requires_privileged_parent=
False,
768def _policy_from_row(row: ShellPolicyRow) -> tuple[str, ShellPolicy]:
769 """Convert one compact domain row into the canonical typed value."""
770 path, security, usage, dialect, executable, privileged_parent = row
771 return path, ShellPolicy(
772 ShellSecurity(security),
774 ShellDialect(dialect),
775 executable=executable,
776 source_requires_privileged_parent=privileged_parent,
780def merge_policy_tables(
781 base: dict[str, ShellPolicy],
782 *domains: tuple[ShellPolicyRow, ...],
783) -> dict[str, ShellPolicy]:
784 """Merge independently reviewable domains, rejecting duplicate authority."""
786 for domain
in domains:
788 path, policy = _policy_from_row(row)
790 raise DuplicateShellPolicyError(path)
791 merged[path] = policy
795SHELL_POLICIES = merge_policy_tables(_BASE_SHELL_POLICIES, CI_POLICY_ROWS, HIL_POLICY_ROWS)
797PRIVILEGED_PATHS = frozenset(
798 path
for path, policy
in SHELL_POLICIES.items()
if policy.security
is ShellSecurity.PRIVILEGED
800SOURCED_ONLY_PATHS = frozenset(
801 path
for path, policy
in SHELL_POLICIES.items()
if policy.usage
is ShellUsage.SOURCED_ONLY