rcf-lite 0.27.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +10 -0
- package/bin/rcf.js +2 -0
- package/blueprints/application-account-settings/CHANGELOG.md +35 -6
- package/blueprints/application-account-settings/README.md +1 -1
- package/blueprints/application-account-settings/blueprint.json +4 -2
- package/blueprints/application-account-settings/contributions/adrs/adr-2602-application-account-settings-security-surface-shape.json +1 -1
- package/blueprints/application-account-settings/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-account-settings/contributions/probes/profile-form-autocomplete.mjs +67 -0
- package/blueprints/application-account-settings/contributions/probes/run-profile-form-autocomplete.mjs +5 -0
- package/blueprints/application-account-settings/contributions/probes/run-sessions-adapter-uniform.mjs +5 -0
- package/blueprints/application-account-settings/contributions/probes/run-sessions-surface-shape.mjs +5 -0
- package/blueprints/application-account-settings/contributions/probes/run-shell-tablist-per-capability.mjs +5 -0
- package/blueprints/application-account-settings/contributions/probes/run-theme-radiogroup.mjs +5 -0
- package/blueprints/application-account-settings/contributions/probes/sessions-adapter-uniform.mjs +130 -0
- package/blueprints/application-account-settings/contributions/probes/sessions-surface-shape.mjs +70 -0
- package/blueprints/application-account-settings/contributions/probes/shell-tablist-per-capability.mjs +60 -0
- package/blueprints/application-account-settings/contributions/probes/theme-radiogroup.mjs +126 -0
- package/blueprints/application-admin-console/CHANGELOG.md +32 -6
- package/blueprints/application-admin-console/README.md +1 -1
- package/blueprints/application-admin-console/blueprint.json +4 -2
- package/blueprints/application-admin-console/contributions/adrs/adr-2201-application-admin-console-capability-vocabulary.json +1 -1
- package/blueprints/application-admin-console/contributions/probes/audit-log-surface.mjs +79 -0
- package/blueprints/application-admin-console/contributions/probes/org-switcher-surface.mjs +83 -0
- package/blueprints/application-admin-console/contributions/probes/permission-matrix-grid.mjs +67 -0
- package/blueprints/application-admin-console/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-admin-console/contributions/probes/run-audit-log-surface.mjs +5 -0
- package/blueprints/application-admin-console/contributions/probes/run-org-switcher-surface.mjs +5 -0
- package/blueprints/application-admin-console/contributions/probes/run-permission-matrix-grid.mjs +5 -0
- package/blueprints/application-admin-console/contributions/probes/run-sign-in-access-gated-surface.mjs +5 -0
- package/blueprints/application-admin-console/contributions/probes/run-users-directory-surface.mjs +5 -0
- package/blueprints/application-admin-console/contributions/probes/sign-in-access-gated-surface.mjs +117 -0
- package/blueprints/application-admin-console/contributions/probes/users-directory-surface.mjs +145 -0
- package/blueprints/application-api-rest/CHANGELOG.md +16 -8
- package/blueprints/application-api-rest/README.md +3 -3
- package/blueprints/application-api-rest/blueprint.json +3 -2
- package/blueprints/application-api-rest/contributions/adrs/adr-302-application-api-rest-auth-model.json +1 -1
- package/blueprints/application-api-rest/contributions/probes/cursor-pagination-round-trip.mjs +154 -0
- package/blueprints/application-api-rest/contributions/probes/health-probes-distinct.mjs +124 -0
- package/blueprints/application-api-rest/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-api-rest/contributions/probes/problem-details-on-error.mjs +77 -0
- package/blueprints/application-api-rest/contributions/probes/request-id-echoed.mjs +76 -0
- package/blueprints/application-api-rest/contributions/probes/run-cursor-pagination-round-trip.mjs +5 -0
- package/blueprints/application-api-rest/contributions/probes/run-health-probes-distinct.mjs +5 -0
- package/blueprints/application-api-rest/contributions/probes/run-problem-details-on-error.mjs +5 -0
- package/blueprints/application-api-rest/contributions/probes/run-request-id-echoed.mjs +5 -0
- package/blueprints/application-api-rest/docs/topics.md +1 -1
- package/blueprints/application-charts/CHANGELOG.md +35 -9
- package/blueprints/application-charts/README.md +6 -6
- package/blueprints/application-charts/blueprint.json +5 -3
- package/blueprints/application-charts/contributions/adrs/adr-1901-application-charts-engine-choice.json +1 -1
- package/blueprints/application-charts/contributions/probes/keyboard-traversal.mjs +151 -0
- package/blueprints/application-charts/contributions/probes/non-colour-distinction.mjs +67 -0
- package/blueprints/application-charts/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-charts/contributions/probes/run-keyboard-traversal.mjs +5 -0
- package/blueprints/application-charts/contributions/probes/run-non-colour-distinction.mjs +5 -0
- package/blueprints/application-charts/contributions/probes/run-text-alternative-table.mjs +5 -0
- package/blueprints/application-charts/contributions/probes/text-alternative-table.mjs +139 -0
- package/blueprints/application-charts/contributions/requirements/application-charts-req-001.json +1 -1
- package/blueprints/application-charts/contributions/requirements/application-charts-req-002.json +1 -1
- package/blueprints/application-charts/contributions/user-stories/application-charts-us-18101.json +5 -5
- package/blueprints/application-charts/contributions/user-stories/application-charts-us-18106.json +1 -1
- package/blueprints/application-charts/guide/application-charts.md +1 -1
- package/blueprints/application-dashboard/CHANGELOG.md +20 -10
- package/blueprints/application-dashboard/README.md +6 -6
- package/blueprints/application-dashboard/assets/guidance/dashboard-design.md +2 -2
- package/blueprints/application-dashboard/blueprint.json +3 -2
- package/blueprints/application-dashboard/contributions/adrs/adr-2001-application-dashboard-primary-kpi-enum.json +1 -1
- package/blueprints/application-dashboard/contributions/adrs/adr-2002-application-dashboard-timeframe-presets.json +1 -1
- package/blueprints/application-dashboard/contributions/probes/export-handle-formats.mjs +75 -0
- package/blueprints/application-dashboard/contributions/probes/four-tile-states.mjs +75 -0
- package/blueprints/application-dashboard/contributions/probes/primary-kpi-top-left.mjs +58 -0
- package/blueprints/application-dashboard/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-dashboard/contributions/probes/run-export-handle-formats.mjs +4 -0
- package/blueprints/application-dashboard/contributions/probes/run-four-tile-states.mjs +4 -0
- package/blueprints/application-dashboard/contributions/probes/run-primary-kpi-top-left.mjs +4 -0
- package/blueprints/application-dashboard/contributions/probes/run-shell-five-regions.mjs +4 -0
- package/blueprints/application-dashboard/contributions/probes/shell-five-regions.mjs +93 -0
- package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-002.json +1 -1
- package/blueprints/application-dashboard/contributions/requirements/application-dashboard-req-003.json +1 -1
- package/blueprints/application-dashboard/contributions/tacs/tac-2002-application-dashboard-filter-chrome.json +1 -1
- package/blueprints/application-dashboard/contributions/tacs/tac-2003-application-dashboard-export-handle.json +2 -2
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19101.json +2 -2
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19102.json +2 -2
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19103.json +1 -1
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19104.json +1 -1
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19105.json +1 -1
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19106.json +2 -2
- package/blueprints/application-dashboard/contributions/user-stories/application-dashboard-us-19107.json +5 -5
- package/blueprints/application-dashboard/docs/topics.md +2 -2
- package/blueprints/application-dashboard/guide/application-dashboard.md +2 -2
- package/blueprints/application-datatable/CHANGELOG.md +19 -9
- package/blueprints/application-datatable/README.md +2 -2
- package/blueprints/application-datatable/blueprint.json +3 -2
- package/blueprints/application-datatable/contributions/probes/apg-table-shape.mjs +57 -0
- package/blueprints/application-datatable/contributions/probes/four-states-regions.mjs +80 -0
- package/blueprints/application-datatable/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-datatable/contributions/probes/run-apg-table-shape.mjs +4 -0
- package/blueprints/application-datatable/contributions/probes/run-four-states-regions.mjs +4 -0
- package/blueprints/application-datatable/contributions/probes/run-search-adapter-round-trip.mjs +4 -0
- package/blueprints/application-datatable/contributions/probes/run-sort-adapter-round-trip.mjs +4 -0
- package/blueprints/application-datatable/contributions/probes/search-adapter-round-trip.mjs +58 -0
- package/blueprints/application-datatable/contributions/probes/sort-adapter-round-trip.mjs +60 -0
- package/blueprints/application-datatable/contributions/requirements/application-datatable-req-006.json +1 -1
- package/blueprints/application-datatable/docs/topics.md +1 -1
- package/blueprints/application-empty-error-states/CHANGELOG.md +33 -3
- package/blueprints/application-empty-error-states/blueprint.json +4 -2
- package/blueprints/application-empty-error-states/contributions/probes/empty-list-and-no-search.mjs +66 -0
- package/blueprints/application-empty-error-states/contributions/probes/error-boundary-alert.mjs +65 -0
- package/blueprints/application-empty-error-states/contributions/probes/forbidden-and-server-error.mjs +167 -0
- package/blueprints/application-empty-error-states/contributions/probes/not-found-and-recovery.mjs +61 -0
- package/blueprints/application-empty-error-states/contributions/probes/permission-denied-and-offline.mjs +155 -0
- package/blueprints/application-empty-error-states/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-empty-error-states/contributions/probes/run-empty-list-and-no-search.mjs +5 -0
- package/blueprints/application-empty-error-states/contributions/probes/run-error-boundary-alert.mjs +5 -0
- package/blueprints/application-empty-error-states/contributions/probes/run-forbidden-and-server-error.mjs +5 -0
- package/blueprints/application-empty-error-states/contributions/probes/run-not-found-and-recovery.mjs +5 -0
- package/blueprints/application-empty-error-states/contributions/probes/run-permission-denied-and-offline.mjs +5 -0
- package/blueprints/application-error-handling/CHANGELOG.md +45 -6
- package/blueprints/application-error-handling/blueprint.json +3 -2
- package/blueprints/application-error-handling/contributions/probes/category-vocabulary.mjs +90 -0
- package/blueprints/application-error-handling/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-error-handling/contributions/probes/record-shape-adr-1701.mjs +78 -0
- package/blueprints/application-error-handling/contributions/probes/run-category-vocabulary.mjs +5 -0
- package/blueprints/application-error-handling/contributions/probes/run-record-shape-adr-1701.mjs +5 -0
- package/blueprints/application-error-handling/contributions/probes/run-two-boundaries-registered.mjs +5 -0
- package/blueprints/application-error-handling/contributions/probes/two-boundaries-registered.mjs +343 -0
- package/blueprints/application-file-upload/CHANGELOG.md +28 -6
- package/blueprints/application-file-upload/README.md +1 -1
- package/blueprints/application-file-upload/blueprint.json +3 -2
- package/blueprints/application-file-upload/contributions/probes/assertive-completion-slot.mjs +78 -0
- package/blueprints/application-file-upload/contributions/probes/chunked-transport-endpoints.mjs +173 -0
- package/blueprints/application-file-upload/contributions/probes/per-file-progressbar.mjs +124 -0
- package/blueprints/application-file-upload/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-file-upload/contributions/probes/run-assertive-completion-slot.mjs +4 -0
- package/blueprints/application-file-upload/contributions/probes/run-chunked-transport-endpoints.mjs +4 -0
- package/blueprints/application-file-upload/contributions/probes/run-per-file-progressbar.mjs +4 -0
- package/blueprints/application-file-upload/contributions/probes/run-upload-surface-shape.mjs +4 -0
- package/blueprints/application-file-upload/contributions/probes/upload-surface-shape.mjs +55 -0
- package/blueprints/application-file-upload/contributions/requirements/application-file-upload-req-001.json +1 -1
- package/blueprints/application-file-upload/contributions/requirements/application-file-upload-req-002.json +1 -1
- package/blueprints/application-file-upload/contributions/requirements/application-file-upload-req-003.json +1 -1
- package/blueprints/application-file-upload/contributions/requirements/application-file-upload-req-004.json +1 -1
- package/blueprints/application-file-upload/contributions/requirements/application-file-upload-req-005.json +1 -1
- package/blueprints/application-file-upload/probe-packs/application-file-upload.pack.mjs +1 -1
- package/blueprints/application-forms-wizard/CHANGELOG.md +20 -6
- package/blueprints/application-forms-wizard/README.md +2 -2
- package/blueprints/application-forms-wizard/blueprint.json +3 -2
- package/blueprints/application-forms-wizard/contributions/probes/draft-persistence-round-trip.mjs +110 -0
- package/blueprints/application-forms-wizard/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-forms-wizard/contributions/probes/run-draft-persistence-round-trip.mjs +4 -0
- package/blueprints/application-forms-wizard/contributions/probes/run-step-page-shape.mjs +4 -0
- package/blueprints/application-forms-wizard/contributions/probes/run-task-list-surface.mjs +4 -0
- package/blueprints/application-forms-wizard/contributions/probes/step-page-shape.mjs +61 -0
- package/blueprints/application-forms-wizard/contributions/probes/task-list-surface.mjs +109 -0
- package/blueprints/application-forms-wizard/contributions/requirements/application-forms-wizard-req-001.json +1 -1
- package/blueprints/application-forms-wizard/contributions/requirements/application-forms-wizard-req-002.json +1 -1
- package/blueprints/application-forms-wizard/contributions/requirements/application-forms-wizard-req-003.json +1 -1
- package/blueprints/application-forms-wizard/contributions/requirements/application-forms-wizard-req-004.json +1 -1
- package/blueprints/application-forms-wizard/contributions/requirements/application-forms-wizard-req-005.json +1 -1
- package/blueprints/application-forms-wizard/probe-packs/application-forms-wizard.pack.mjs +1 -1
- package/blueprints/application-notifications-in-app/CHANGELOG.md +38 -5
- package/blueprints/application-notifications-in-app/README.md +4 -4
- package/blueprints/application-notifications-in-app/blueprint.json +4 -2
- package/blueprints/application-notifications-in-app/contributions/adrs/adr-2101-application-notifications-in-app-live-region-role-mapping.json +1 -1
- package/blueprints/application-notifications-in-app/contributions/probes/centre-acknowledge-round-trip.mjs +210 -0
- package/blueprints/application-notifications-in-app/contributions/probes/live-region-preseeding.mjs +93 -0
- package/blueprints/application-notifications-in-app/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-notifications-in-app/contributions/probes/run-centre-acknowledge-round-trip.mjs +5 -0
- package/blueprints/application-notifications-in-app/contributions/probes/run-live-region-preseeding.mjs +5 -0
- package/blueprints/application-notifications-in-app/contributions/probes/run-toast-contract.mjs +5 -0
- package/blueprints/application-notifications-in-app/contributions/probes/toast-contract.mjs +92 -0
- package/blueprints/application-notifications-in-app/contributions/tacs/tac-2101-application-notifications-in-app-live-region.json +3 -3
- package/blueprints/application-notifications-in-app/contributions/tacs/tac-2102-application-notifications-in-app-centre.json +1 -1
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20101.json +1 -1
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20102.json +1 -1
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20103.json +1 -1
- package/blueprints/application-notifications-in-app/contributions/user-stories/application-notifications-in-app-us-20105.json +1 -1
- package/blueprints/application-notifications-in-app/guide/application-notifications-in-app.md +3 -3
- package/blueprints/application-onboarding-tour/CHANGELOG.md +31 -5
- package/blueprints/application-onboarding-tour/README.md +1 -1
- package/blueprints/application-onboarding-tour/blueprint.json +5 -3
- package/blueprints/application-onboarding-tour/contributions/probes/checklist-anchor-open.mjs +81 -0
- package/blueprints/application-onboarding-tour/contributions/probes/completion-persistence.mjs +159 -0
- package/blueprints/application-onboarding-tour/contributions/probes/first-run-detection.mjs +67 -0
- package/blueprints/application-onboarding-tour/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-onboarding-tour/contributions/probes/run-checklist-anchor-open.mjs +5 -0
- package/blueprints/application-onboarding-tour/contributions/probes/run-completion-persistence.mjs +5 -0
- package/blueprints/application-onboarding-tour/contributions/probes/run-first-run-detection.mjs +5 -0
- package/blueprints/application-onboarding-tour/contributions/probes/run-stepper-role-dialog.mjs +5 -0
- package/blueprints/application-onboarding-tour/contributions/probes/stepper-role-dialog.mjs +45 -0
- package/blueprints/application-spa/CHANGELOG.md +20 -11
- package/blueprints/application-spa/README.md +9 -10
- package/blueprints/application-spa/blueprint.json +3 -2
- package/blueprints/application-spa/contributions/adrs/adr-202-application-spa-theming.json +1 -1
- package/blueprints/application-spa/contributions/adrs/adr-206-application-spa-iconography.json +1 -1
- package/blueprints/application-spa/contributions/probes/designed-empty-state.mjs +72 -0
- package/blueprints/application-spa/contributions/probes/probe-utils.mjs +177 -0
- package/blueprints/application-spa/contributions/probes/route-inventory-published.mjs +185 -0
- package/blueprints/application-spa/contributions/probes/run-designed-empty-state.mjs +5 -0
- package/blueprints/application-spa/contributions/probes/run-route-inventory-published.mjs +5 -0
- package/blueprints/application-spa/contributions/probes/run-shell-nav-present.mjs +5 -0
- package/blueprints/application-spa/contributions/probes/shell-nav-present.mjs +69 -0
- package/blueprints/application-spa/contributions/tacs/tac-207-application-spa-token-adherence-probe.json +1 -1
- package/blueprints/application-spa/contributions/tacs/tac-208-application-spa-icon-adherence-probe.json +2 -2
- package/blueprints/application-spa/contributions/tacs/tac-209-application-spa-csp-styled-adherence-probe.json +3 -3
- package/blueprints/application-spa/contributions/tacs/tac-210-application-spa-external-dependency-provisioning-probe.json +2 -2
- package/blueprints/application-spa/contributions/tacs/tac-211-application-spa-core-flow-e2e-probe.json +2 -2
- package/blueprints/application-spa/contributions/user-stories/application-spa-us-1131.json +1 -1
- package/blueprints/application-spa/docs/topics.md +1 -1
- package/blueprints/delivery-ci-workflows/CHANGELOG.md +31 -11
- package/blueprints/delivery-ci-workflows/README.md +3 -3
- package/blueprints/delivery-ci-workflows/blueprint.json +3 -2
- package/blueprints/delivery-ci-workflows/contributions/adrs/adr-702-delivery-ci-workflows-strict-coverage-gate.json +2 -2
- package/blueprints/delivery-ci-workflows/contributions/probes/node-gate-entrypoint.mjs +76 -0
- package/blueprints/delivery-ci-workflows/contributions/probes/probe-utils.mjs +56 -0
- package/blueprints/delivery-ci-workflows/contributions/probes/real-account-github-actions-run-record.mjs +139 -0
- package/blueprints/delivery-ci-workflows/contributions/probes/run-node-gate-entrypoint.mjs +3 -0
- package/blueprints/delivery-ci-workflows/contributions/probes/run-real-account-github-actions-run-record.mjs +3 -0
- package/blueprints/delivery-ci-workflows/contributions/probes/run-workflow-template-shape.mjs +3 -0
- package/blueprints/delivery-ci-workflows/contributions/probes/workflow-template-shape.mjs +94 -0
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-004.json +1 -1
- package/blueprints/delivery-ci-workflows/docs/topics.md +2 -3
- package/blueprints/deploy-cloudflare-workers/CHANGELOG.md +4 -4
- package/blueprints/deploy-cloudflare-workers/README.md +3 -3
- package/blueprints/deploy-cloudflare-workers/contributions/adrs/adr-1306-deploy-cloudflare-workers-spa-shape.json +2 -2
- package/blueprints/deploy-cloudflare-workers/docs/topics.md +2 -2
- package/blueprints/deploy-hetzner-server/CHANGELOG.md +98 -7
- package/blueprints/deploy-hetzner-server/blueprint.json +7 -6
- package/blueprints/deploy-hetzner-server/contributions/probes/cloud-init-render-lint.mjs +46 -21
- package/blueprints/deploy-hetzner-server/contributions/probes/hcloud-dry-run-mock.mjs +344 -83
- package/blueprints/deploy-hetzner-server/contributions/probes/manifest-schema-validate.mjs +211 -42
- package/blueprints/deploy-hetzner-server/contributions/probes/probe-utils.mjs +140 -22
- package/blueprints/deploy-hetzner-server/contributions/probes/real-account-cloud-init-hardened.mjs +111 -30
- package/blueprints/deploy-hetzner-server/contributions/probes/real-account-snapshot-on-demand.mjs +158 -30
- package/blueprints/deploy-hetzner-server/contributions/probes/real-account-throwaway-server-provision.mjs +154 -32
- package/blueprints/edge-cloudflare-access/CHANGELOG.md +6 -5
- package/blueprints/edge-cloudflare-access/blueprint.json +1 -1
- package/blueprints/edge-cloudflare-access/contributions/probes/admin-console-gate-surface.mjs +9 -1
- package/blueprints/edge-cloudflare-access/docs/topics.md +1 -1
- package/blueprints/edge-cloudflare-rate-limiting/CHANGELOG.md +4 -7
- package/blueprints/edge-cloudflare-rate-limiting/docs/topics.md +1 -1
- package/blueprints/edge-cloudflare-tunnel/CHANGELOG.md +4 -6
- package/blueprints/edge-cloudflare-tunnel/README.md +1 -2
- package/blueprints/edge-cloudflare-tunnel/docs/topics.md +1 -1
- package/blueprints/edge-cloudflare-turnstile/CHANGELOG.md +2 -3
- package/blueprints/edge-cloudflare-turnstile/docs/topics.md +1 -1
- package/blueprints/email-smtp-resend/CHANGELOG.md +24 -8
- package/blueprints/email-smtp-resend/README.md +2 -2
- package/blueprints/email-smtp-resend/blueprint.json +3 -2
- package/blueprints/email-smtp-resend/contributions/probes/probe-utils.mjs +50 -0
- package/blueprints/email-smtp-resend/contributions/probes/real-account-resend-send.mjs +50 -0
- package/blueprints/email-smtp-resend/contributions/probes/run-real-account-resend-send.mjs +3 -0
- package/blueprints/email-smtp-resend/contributions/probes/run-smtp-round-trip.mjs +3 -0
- package/blueprints/email-smtp-resend/contributions/probes/run-unverified-sender-refusal.mjs +3 -0
- package/blueprints/email-smtp-resend/contributions/probes/smtp-round-trip.mjs +43 -0
- package/blueprints/email-smtp-resend/contributions/probes/unverified-sender-refusal.mjs +99 -0
- package/blueprints/email-smtp-resend/docs/topics.md +2 -3
- package/blueprints/jobs-background/CHANGELOG.md +66 -5
- package/blueprints/jobs-background/README.md +2 -2
- package/blueprints/jobs-background/blueprint.json +3 -2
- package/blueprints/jobs-background/contributions/probes/apply-time-override.mjs +38 -10
- package/blueprints/jobs-background/contributions/probes/apply-time-refusal.mjs +28 -6
- package/blueprints/jobs-background/contributions/probes/event-secrecy.mjs +16 -2
- package/blueprints/jobs-background/contributions/probes/fake-clock-cron.mjs +102 -21
- package/blueprints/jobs-background/contributions/probes/probe-utils.mjs +10 -2
- package/blueprints/jobs-background/contributions/probes/retry-and-fail-real-account.mjs +355 -0
- package/blueprints/jobs-background/contributions/probes/retry-and-fail.mjs +33 -2
- package/blueprints/jobs-background/contributions/probes/run-retry-and-fail-real-account.mjs +12 -0
- package/blueprints/jobs-background/docs/topics.md +1 -1
- package/blueprints/jobs-background/guide/jobs-background.md +1 -1
- package/blueprints/messaging-queue-cloudflare/CHANGELOG.md +25 -19
- package/blueprints/messaging-queue-cloudflare/README.md +12 -12
- package/blueprints/messaging-queue-cloudflare/blueprint.json +1 -1
- package/blueprints/messaging-queue-cloudflare/contributions/adrs/adr-3001-messaging-queue-cloudflare-adapter.json +2 -2
- package/blueprints/messaging-queue-cloudflare/contributions/adrs/adr-3002-messaging-queue-cloudflare-delivery-semantics.json +1 -1
- package/blueprints/messaging-queue-cloudflare/contributions/probes/real-account-concurrency-smoke.mjs +180 -12
- package/blueprints/messaging-queue-cloudflare/contributions/requirements/messaging-queue-cloudflare-req-006.json +1 -1
- package/blueprints/messaging-queue-cloudflare/docs/topics.md +2 -2
- package/blueprints/messaging-queue-cloudflare/guide/messaging-queue-cloudflare.md +2 -2
- package/blueprints/object-storage-s3/CHANGELOG.md +75 -4
- package/blueprints/object-storage-s3/blueprint.json +3 -2
- package/blueprints/object-storage-s3/contributions/probes/event-secrecy.mjs +73 -12
- package/blueprints/object-storage-s3/contributions/probes/facade-round-trip.mjs +55 -7
- package/blueprints/object-storage-s3/contributions/probes/hetzner-object-storage-round-trip.mjs +102 -18
- package/blueprints/object-storage-s3/contributions/probes/multipart-upload.mjs +105 -36
- package/blueprints/object-storage-s3/contributions/probes/presigned-url.mjs +104 -55
- package/blueprints/object-storage-s3/contributions/probes/probe-utils.mjs +129 -7
- package/blueprints/object-storage-s3/contributions/probes/put-get-round-trip.mjs +80 -18
- package/blueprints/object-storage-s3/contributions/probes/r2-real-account-smoke.mjs +291 -37
- package/blueprints/object-storage-s3/contributions/probes/run-event-secrecy.mjs +8 -2
- package/blueprints/object-storage-s3/contributions/probes/run-facade-round-trip.mjs +5 -5
- package/blueprints/object-storage-s3/contributions/probes/run-hetzner-object-storage-round-trip.mjs +8 -5
- package/blueprints/object-storage-s3/contributions/probes/run-multipart-upload.mjs +8 -2
- package/blueprints/object-storage-s3/contributions/probes/run-presigned-url.mjs +8 -2
- package/blueprints/object-storage-s3/contributions/probes/run-put-get-round-trip.mjs +6 -3
- package/blueprints/object-storage-s3/contributions/probes/run-r2-real-account-smoke.mjs +10 -2
- package/blueprints/object-storage-s3/contributions/requirements/object-storage-s3-req-006.json +1 -1
- package/blueprints/object-storage-s3/docs/topics.md +1 -1
- package/blueprints/object-storage-s3/guide/object-storage-s3.md +1 -1
- package/blueprints/observability-essentials/CHANGELOG.md +23 -7
- package/blueprints/observability-essentials/README.md +2 -2
- package/blueprints/observability-essentials/assets/probe-samples/probe-response-baseline.v2.json +1 -1
- package/blueprints/observability-essentials/blueprint.json +3 -2
- package/blueprints/observability-essentials/contributions/probes/liveness-probe.mjs +84 -0
- package/blueprints/observability-essentials/contributions/probes/metrics-endpoint.mjs +96 -0
- package/blueprints/observability-essentials/contributions/probes/probe-utils.mjs +52 -0
- package/blueprints/observability-essentials/contributions/probes/readiness-probe.mjs +171 -0
- package/blueprints/observability-essentials/contributions/probes/run-liveness-probe.mjs +3 -0
- package/blueprints/observability-essentials/contributions/probes/run-metrics-endpoint.mjs +3 -0
- package/blueprints/observability-essentials/contributions/probes/run-readiness-probe.mjs +3 -0
- package/blueprints/observability-essentials/docs/topics.md +2 -3
- package/blueprints/observability-logging/CHANGELOG.md +29 -4
- package/blueprints/observability-logging/README.md +2 -2
- package/blueprints/observability-logging/blueprint.json +3 -2
- package/blueprints/observability-logging/contributions/probes/correlation-id-flow.mjs +173 -0
- package/blueprints/observability-logging/contributions/probes/line-shape-and-fields.mjs +82 -0
- package/blueprints/observability-logging/contributions/probes/probe-utils.mjs +47 -0
- package/blueprints/observability-logging/contributions/probes/redaction-boundary.mjs +130 -0
- package/blueprints/observability-logging/contributions/probes/run-correlation-id-flow.mjs +3 -0
- package/blueprints/observability-logging/contributions/probes/run-line-shape-and-fields.mjs +3 -0
- package/blueprints/observability-logging/contributions/probes/run-redaction-boundary.mjs +3 -0
- package/blueprints/observability-probe-endpoints/CHANGELOG.md +23 -8
- package/blueprints/observability-probe-endpoints/README.md +3 -3
- package/blueprints/observability-probe-endpoints/blueprint.json +3 -2
- package/blueprints/observability-probe-endpoints/contributions/probes/kubernetes-startup-enabled.mjs +80 -0
- package/blueprints/observability-probe-endpoints/contributions/probes/partial-profile-refusal.mjs +101 -0
- package/blueprints/observability-probe-endpoints/contributions/probes/probe-utils.mjs +49 -0
- package/blueprints/observability-probe-endpoints/contributions/probes/profile-boot-materialisation.mjs +69 -0
- package/blueprints/observability-probe-endpoints/contributions/probes/run-kubernetes-startup-enabled.mjs +3 -0
- package/blueprints/observability-probe-endpoints/contributions/probes/run-partial-profile-refusal.mjs +3 -0
- package/blueprints/observability-probe-endpoints/contributions/probes/run-profile-boot-materialisation.mjs +3 -0
- package/blueprints/observability-probe-endpoints/docs/topics.md +2 -2
- package/blueprints/persistence-data-d1/CHANGELOG.md +16 -3
- package/blueprints/persistence-data-d1/README.md +2 -2
- package/blueprints/persistence-data-d1/blueprint.json +3 -2
- package/blueprints/persistence-data-d1/contributions/adrs/adr-1404-persistence-data-d1-store-boundary.json +1 -1
- package/blueprints/persistence-data-d1/contributions/probes/facade-round-trip.mjs +140 -0
- package/blueprints/persistence-data-d1/contributions/probes/migrations-forward-only.mjs +66 -0
- package/blueprints/persistence-data-d1/contributions/probes/probe-utils.mjs +51 -0
- package/blueprints/persistence-data-d1/contributions/probes/real-account-d1-round-trip.mjs +133 -0
- package/blueprints/persistence-data-d1/contributions/probes/run-facade-round-trip.mjs +3 -0
- package/blueprints/persistence-data-d1/contributions/probes/run-migrations-forward-only.mjs +3 -0
- package/blueprints/persistence-data-d1/contributions/probes/run-real-account-d1-round-trip.mjs +3 -0
- package/blueprints/persistence-data-d1/docs/topics.md +2 -3
- package/blueprints/persistence-data-postgres/CHANGELOG.md +84 -4
- package/blueprints/persistence-data-postgres/README.md +2 -2
- package/blueprints/persistence-data-postgres/blueprint.json +3 -2
- package/blueprints/persistence-data-postgres/contributions/adrs/adr-2801-persistence-data-postgres-driver.json +1 -1
- package/blueprints/persistence-data-postgres/contributions/probes/facade-round-trip.mjs +87 -12
- package/blueprints/persistence-data-postgres/contributions/probes/migration-apply.mjs +245 -19
- package/blueprints/persistence-data-postgres/contributions/probes/pool-posture-smoke.mjs +109 -26
- package/blueprints/persistence-data-postgres/contributions/probes/prepared-statement-scan.mjs +151 -38
- package/blueprints/persistence-data-postgres/contributions/probes/probe-utils.mjs +24 -6
- package/blueprints/persistence-data-postgres/contributions/probes/recovery-restore-round-trip.mjs +261 -48
- package/blueprints/persistence-data-postgres/contributions/probes/transaction-atomicity.mjs +76 -20
- package/blueprints/persistence-data-postgres/contributions/requirements/persistence-data-postgres-req-003.json +1 -1
- package/blueprints/persistence-data-postgres/docs/topics.md +1 -1
- package/blueprints/persistence-data-sqlite/CHANGELOG.md +16 -2
- package/blueprints/persistence-data-sqlite/README.md +2 -2
- package/blueprints/persistence-data-sqlite/blueprint.json +3 -2
- package/blueprints/persistence-data-sqlite/contributions/probes/boot-open-migrate.mjs +90 -0
- package/blueprints/persistence-data-sqlite/contributions/probes/facade-round-trip.mjs +82 -0
- package/blueprints/persistence-data-sqlite/contributions/probes/probe-utils.mjs +83 -0
- package/blueprints/persistence-data-sqlite/contributions/probes/run-boot-open-migrate.mjs +5 -0
- package/blueprints/persistence-data-sqlite/contributions/probes/run-facade-round-trip.mjs +5 -0
- package/blueprints/persistence-data-sqlite/contributions/probes/run-wal-checkpoint.mjs +5 -0
- package/blueprints/persistence-data-sqlite/contributions/probes/wal-checkpoint.mjs +137 -0
- package/blueprints/persistence-data-sqlite/docs/topics.md +2 -3
- package/blueprints/platform-cloudflare-cron-triggers/CHANGELOG.md +5 -5
- package/blueprints/platform-cloudflare-cron-triggers/README.md +2 -3
- package/blueprints/platform-cloudflare-cron-triggers/docs/topics.md +1 -1
- package/blueprints/platform-cloudflare-durable-objects/CHANGELOG.md +6 -10
- package/blueprints/platform-cloudflare-durable-objects/docs/topics.md +1 -1
- package/blueprints/platform-cloudflare-kv/CHANGELOG.md +16 -12
- package/blueprints/platform-cloudflare-kv/README.md +2 -4
- package/blueprints/platform-cloudflare-kv/blueprint.json +2 -2
- package/blueprints/platform-cloudflare-kv/docs/topics.md +1 -1
- package/blueprints/platform-docker-compose-host/CHANGELOG.md +92 -5
- package/blueprints/platform-docker-compose-host/blueprint.json +2 -2
- package/blueprints/platform-docker-compose-host/contributions/probes/caddyfile-validate.mjs +116 -29
- package/blueprints/platform-docker-compose-host/contributions/probes/compose-config-lint.mjs +181 -109
- package/blueprints/platform-docker-compose-host/contributions/probes/probe-utils.mjs +146 -26
- package/blueprints/platform-docker-compose-host/contributions/probes/real-account-minimal-stack-up.mjs +238 -24
- package/blueprints/platform-docker-compose-host/contributions/probes/real-account-reload-burst.mjs +207 -21
- package/blueprints/platform-docker-compose-host/contributions/probes/secrets-as-files-scan.mjs +246 -42
- package/blueprints/platform-docker-compose-host/docs/topics.md +1 -1
- package/blueprints/security-auth-clerk/CHANGELOG.md +18 -6
- package/blueprints/security-auth-clerk/README.md +3 -3
- package/blueprints/security-auth-clerk/assets/wiring/workers-wrangler-toml-shape.md +1 -1
- package/blueprints/security-auth-clerk/blueprint.json +3 -2
- package/blueprints/security-auth-clerk/contributions/probes/hosted-identity-ui-config.mjs +61 -0
- package/blueprints/security-auth-clerk/contributions/probes/probe-utils.mjs +188 -0
- package/blueprints/security-auth-clerk/contributions/probes/real-account-principal-directory-round-trip.mjs +216 -0
- package/blueprints/security-auth-clerk/contributions/probes/real-account-session-inventory.mjs +260 -0
- package/blueprints/security-auth-clerk/contributions/probes/role-model-adapter.mjs +53 -0
- package/blueprints/security-auth-clerk/contributions/probes/run-hosted-identity-ui-config.mjs +4 -0
- package/blueprints/security-auth-clerk/contributions/probes/run-real-account-principal-directory-round-trip.mjs +4 -0
- package/blueprints/security-auth-clerk/contributions/probes/run-real-account-session-inventory.mjs +4 -0
- package/blueprints/security-auth-clerk/contributions/probes/run-role-model-adapter.mjs +4 -0
- package/blueprints/security-auth-clerk/docs/topics.md +2 -3
- package/blueprints/security-auth-keycloak/CHANGELOG.md +18 -7
- package/blueprints/security-auth-keycloak/README.md +2 -2
- package/blueprints/security-auth-keycloak/blueprint.json +3 -2
- package/blueprints/security-auth-keycloak/contributions/probes/discovery-shape.mjs +68 -0
- package/blueprints/security-auth-keycloak/contributions/probes/introspection-shape.mjs +88 -0
- package/blueprints/security-auth-keycloak/contributions/probes/jwt-verifier-shape.mjs +86 -0
- package/blueprints/security-auth-keycloak/contributions/probes/probe-utils.mjs +180 -0
- package/blueprints/security-auth-keycloak/contributions/probes/real-account-realm-round-trip.mjs +91 -0
- package/blueprints/security-auth-keycloak/contributions/probes/role-adapter-shape.mjs +69 -0
- package/blueprints/security-auth-keycloak/contributions/probes/run-discovery-shape.mjs +4 -0
- package/blueprints/security-auth-keycloak/contributions/probes/run-introspection-shape.mjs +4 -0
- package/blueprints/security-auth-keycloak/contributions/probes/run-jwt-verifier-shape.mjs +4 -0
- package/blueprints/security-auth-keycloak/contributions/probes/run-real-account-realm-round-trip.mjs +4 -0
- package/blueprints/security-auth-keycloak/contributions/probes/run-role-adapter-shape.mjs +4 -0
- package/blueprints/security-auth-keycloak/docs/topics.md +1 -2
- package/blueprints/security-auth-magic-link/CHANGELOG.md +15 -4
- package/blueprints/security-auth-magic-link/README.md +2 -2
- package/blueprints/security-auth-magic-link/assets/principal-registry-samples/single-address.md +1 -1
- package/blueprints/security-auth-magic-link/blueprint.json +3 -2
- package/blueprints/security-auth-magic-link/contributions/probes/probe-utils.mjs +174 -0
- package/blueprints/security-auth-magic-link/contributions/probes/real-account-magic-link-send.mjs +139 -0
- package/blueprints/security-auth-magic-link/contributions/probes/run-real-account-magic-link-send.mjs +4 -0
- package/blueprints/security-auth-magic-link/contributions/probes/run-token-entropy-shape.mjs +4 -0
- package/blueprints/security-auth-magic-link/contributions/probes/run-token-issue-verify.mjs +4 -0
- package/blueprints/security-auth-magic-link/contributions/probes/token-entropy-shape.mjs +60 -0
- package/blueprints/security-auth-magic-link/contributions/probes/token-issue-verify.mjs +90 -0
- package/blueprints/security-auth-magic-link/docs/topics.md +1 -1
- package/blueprints/security-auth-oauth2/CHANGELOG.md +16 -5
- package/blueprints/security-auth-oauth2/README.md +2 -2
- package/blueprints/security-auth-oauth2/blueprint.json +3 -2
- package/blueprints/security-auth-oauth2/contributions/probes/authorisation-code-flow-shape.mjs +259 -0
- package/blueprints/security-auth-oauth2/contributions/probes/pkce-challenge-shape.mjs +58 -0
- package/blueprints/security-auth-oauth2/contributions/probes/probe-utils.mjs +190 -0
- package/blueprints/security-auth-oauth2/contributions/probes/provider-adapter-shape.mjs +64 -0
- package/blueprints/security-auth-oauth2/contributions/probes/real-account-authorisation-code-flow.mjs +93 -0
- package/blueprints/security-auth-oauth2/contributions/probes/run-authorisation-code-flow-shape.mjs +4 -0
- package/blueprints/security-auth-oauth2/contributions/probes/run-pkce-challenge-shape.mjs +4 -0
- package/blueprints/security-auth-oauth2/contributions/probes/run-provider-adapter-shape.mjs +4 -0
- package/blueprints/security-auth-oauth2/contributions/probes/run-real-account-authorisation-code-flow.mjs +4 -0
- package/blueprints/security-auth-oauth2/contributions/probes/run-session-bridge-shape.mjs +4 -0
- package/blueprints/security-auth-oauth2/contributions/probes/session-bridge-shape.mjs +78 -0
- package/blueprints/security-auth-oauth2/docs/topics.md +1 -2
- package/blueprints/security-secrets-management/CHANGELOG.md +15 -4
- package/blueprints/security-secrets-management/README.md +2 -2
- package/blueprints/security-secrets-management/blueprint.json +3 -2
- package/blueprints/security-secrets-management/contributions/probes/add-recipient-rotation.mjs +107 -0
- package/blueprints/security-secrets-management/contributions/probes/encrypt-decrypt-round-trip.mjs +122 -0
- package/blueprints/security-secrets-management/contributions/probes/key-rotation.mjs +93 -0
- package/blueprints/security-secrets-management/contributions/probes/mismatched-key-refusal.mjs +74 -0
- package/blueprints/security-secrets-management/contributions/probes/probe-utils.mjs +214 -0
- package/blueprints/security-secrets-management/contributions/probes/run-add-recipient-rotation.mjs +4 -0
- package/blueprints/security-secrets-management/contributions/probes/run-encrypt-decrypt-round-trip.mjs +4 -0
- package/blueprints/security-secrets-management/contributions/probes/run-key-rotation.mjs +4 -0
- package/blueprints/security-secrets-management/contributions/probes/run-mismatched-key-refusal.mjs +4 -0
- package/blueprints/security-secrets-management/docs/topics.md +2 -3
- package/fixtures/canary-manifest.json +9 -9
- package/guidance/harness-template.md +25 -0
- package/guidance/managed/agent-instructions-block.hash +1 -1
- package/guidance/managed/agent-instructions-block.md +25 -0
- package/package.json +2 -2
- package/rcf/adrs/adr-4101-feedback-store-gitignored.json +20 -0
- package/rcf/adrs/adr-4102-fingerprint-and-labels.json +25 -0
- package/rcf/adrs/adr-4103-redaction-rule-based.json +20 -0
- package/rcf/adrs/adr-4104-destination-on-library.json +25 -0
- package/rcf/adrs/adr-4105-gh-ambient-auth.json +20 -0
- package/rcf/adrs/adr-4106-bundled-entries-terminal.json +20 -0
- package/rcf/adrs/adr-4107-stop-is-ask-event.json +30 -0
- package/rcf/adrs/adr-4108-hooks-in-committed-config.json +25 -0
- package/rcf/adrs/adr-4109-referee-validate-adr-crosslinks.json +25 -0
- package/rcf/adrs/adr-4110-referee-coverage-strict-by-default.json +25 -0
- package/rcf/adrs/adr-4111-referee-finalise-refusals.json +20 -0
- package/rcf/adrs/adr-4112-referee-finalise-verified-invariant.json +30 -0
- package/rcf/code-nodes/cn-600.json +45 -0
- package/rcf/code-nodes/cn-601.json +18 -0
- package/rcf/code-nodes/cn-602.json +15 -0
- package/rcf/code-nodes/cn-603.json +12 -0
- package/rcf/code-nodes/cn-604.json +14 -0
- package/rcf/code-nodes/cn-605.json +17 -0
- package/rcf/code-nodes/cn-606.json +12 -0
- package/rcf/code-nodes/cn-607.json +12 -0
- package/rcf/code-nodes/cn-608.json +14 -0
- package/rcf/code-nodes/cn-609.json +17 -0
- package/rcf/code-nodes/cn-620.json +18 -0
- package/rcf/code-nodes/cn-621.json +14 -0
- package/rcf/code-nodes/cn-622.json +15 -0
- package/rcf/code-nodes/cn-623.json +16 -0
- package/rcf/code-nodes/cn-624.json +14 -0
- package/rcf/code-nodes/cn-625.json +15 -0
- package/rcf/code-nodes/cn-626.json +13 -0
- package/rcf/code-nodes/cn-627.json +13 -0
- package/rcf/code-nodes/cn-628.json +14 -0
- package/rcf/code-nodes/cn-629.json +12 -0
- package/rcf/code-nodes/cn-630.json +12 -0
- package/rcf/code-nodes/cn-631.json +13 -0
- package/rcf/code-nodes/cn-632.json +13 -0
- package/rcf/fbs/fbs-180.json +45 -0
- package/rcf/fbs/fbs-181.json +45 -0
- package/rcf/fbs/fbs-182.json +42 -0
- package/rcf/fbs/fbs-183.json +50 -0
- package/rcf/fbs/fbs-184.json +49 -0
- package/rcf/fbs/fbs-185.json +34 -0
- package/rcf/fbs/fbs-186.json +16 -0
- package/rcf/fbs/fbs-187.json +17 -0
- package/rcf/fbs/fbs-188.json +17 -0
- package/rcf/fbs/fbs-189.json +17 -0
- package/rcf/requirements/req-155.json +13 -0
- package/rcf/requirements/req-156.json +13 -0
- package/rcf/requirements/req-157.json +13 -0
- package/rcf/requirements/req-158.json +13 -0
- package/rcf/requirements/req-159.json +13 -0
- package/rcf/requirements/req-160.json +13 -0
- package/rcf/requirements/req-161.json +13 -0
- package/rcf/requirements/req-162.json +13 -0
- package/rcf/requirements/req-163.json +13 -0
- package/rcf/requirements/req-164.json +13 -0
- package/rcf/requirements/req-165.json +13 -0
- package/rcf/tacs/tac-4101-feedback-cli.json +77 -0
- package/rcf/tacs/tac-4102-feedback-store.json +50 -0
- package/rcf/tacs/tac-4103-feedback-redact.json +39 -0
- package/rcf/tacs/tac-4104-feedback-fingerprint.json +33 -0
- package/rcf/tacs/tac-4105-feedback-render.json +45 -0
- package/rcf/tacs/tac-4106-feedback-destination.json +39 -0
- package/rcf/tacs/tac-4107-feedback-gh.json +43 -0
- package/rcf/tacs/tac-4108-feedback-hook.json +46 -0
- package/rcf/tacs/tac-4109-referee-validate-adr-crosslinks.json +20 -0
- package/rcf/tacs/tac-4110-referee-coverage-mode-selector.json +21 -0
- package/rcf/tacs/tac-4111-referee-finalise-per-ac-refusals.json +21 -0
- package/rcf/tacs/tac-4112-referee-finalise-verified-invariant.json +22 -0
- package/rcf/test-suites/ts-140.json +9 -9
- package/rcf/test-suites/ts-150.json +9 -9
- package/rcf/test-suites/ts-185.json +54 -0
- package/rcf/test-suites/ts-186.json +58 -0
- package/rcf/test-suites/ts-187.json +44 -0
- package/rcf/test-suites/ts-188.json +166 -0
- package/rcf/test-suites/ts-189.json +38 -0
- package/rcf/test-suites/ts-190.json +30 -0
- package/rcf/test-suites/ts-191.json +51 -0
- package/rcf/test-suites/ts-192.json +37 -0
- package/rcf/test-suites/ts-193.json +60 -0
- package/rcf/test-suites/ts-194.json +67 -0
- package/rcf/test-suites/ts-195.json +52 -0
- package/rcf/test-suites/ts-196.json +58 -0
- package/rcf/test-suites/ts-197.json +66 -0
- package/rcf/test-suites/ts-198.json +87 -0
- package/rcf/test-suites/ts-199.json +51 -0
- package/rcf/test-suites/ts-200.json +30 -0
- package/rcf/test-suites/ts-201.json +38 -0
- package/rcf/test-suites/ts-202.json +38 -0
- package/rcf/test-suites/ts-203.json +38 -0
- package/rcf/user-stories/us-15501.json +60 -0
- package/rcf/user-stories/us-15502.json +33 -0
- package/rcf/user-stories/us-15503.json +33 -0
- package/rcf/user-stories/us-15601.json +51 -0
- package/rcf/user-stories/us-15701.json +42 -0
- package/rcf/user-stories/us-15801.json +42 -0
- package/rcf/user-stories/us-15802.json +33 -0
- package/rcf/user-stories/us-15901.json +33 -0
- package/rcf/user-stories/us-15902.json +51 -0
- package/rcf/user-stories/us-16001.json +51 -0
- package/rcf/user-stories/us-16101.json +42 -0
- package/rcf/user-stories/us-16102.json +33 -0
- package/rcf/user-stories/us-16103.json +42 -0
- package/rcf/user-stories/us-16104.json +42 -0
- package/rcf/user-stories/us-16201.json +33 -0
- package/rcf/user-stories/us-16301.json +36 -0
- package/rcf/user-stories/us-16401.json +45 -0
- package/rcf/user-stories/us-16501.json +45 -0
- package/rcf/user-stories/us-16502.json +45 -0
- package/releases/releases.yaml +11 -1
- package/src/blueprint/library-loader.js +48 -0
- package/src/blueprint/library-registry.js +13 -0
- package/src/browser-verify/pack-runner.js +7 -12
- package/src/build/mark.js +1 -1
- package/src/cli/blueprint-library.js +149 -0
- package/src/cli/coverage.js +49 -24
- package/src/cli/doctor.js +195 -7
- package/src/cli/feedback.js +2204 -0
- package/src/cli/finalise.js +158 -1
- package/src/cli/help.js +6 -0
- package/src/cli/init.js +63 -0
- package/src/cli/update.js +21 -1
- package/src/core/store/walker.js +33 -0
- package/src/core/store/writer.js +23 -0
- package/src/feedback/destination.js +361 -0
- package/src/feedback/fingerprint.js +132 -0
- package/src/feedback/gh.js +376 -0
- package/src/feedback/hook.js +183 -0
- package/src/feedback/labels.js +87 -0
- package/src/feedback/redact-allowlist.json +14 -0
- package/src/feedback/redact.js +1108 -0
- package/src/feedback/render.js +267 -0
- package/src/feedback/store.js +604 -0
- package/src/finalise/index.js +4 -0
- package/src/finalise/ingest.js +61 -0
- package/src/finalise/ship-without-eval.js +19 -7
- package/src/mcp/tools.js +8 -2
- package/src/setup/feedback-hooks.js +397 -0
- package/src/setup/managed-gitignore.js +3 -0
- package/src/view/html-page.js +5 -5
- package/src/view/style.css +2 -2
package/blueprints/security-secrets-management/contributions/probes/add-recipient-rotation.mjs
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Add-recipient rotation probe. Encrypts a scratch scope with age
|
|
2
|
+
// recipient A; adds age recipient B via sops --rotate --add-age;
|
|
3
|
+
// asserts the ciphertext's sops.age[] list now includes both
|
|
4
|
+
// recipients; asserts recipient B can decrypt the file with only
|
|
5
|
+
// its own age key. Proves the shipped rotation verb both extends
|
|
6
|
+
// the recipient list and re-wraps the data key against the new
|
|
7
|
+
// recipient.
|
|
8
|
+
//
|
|
9
|
+
// capability: secretsProvider.
|
|
10
|
+
// Anchor honesty. No AC or REQ observes the SOPS-native
|
|
11
|
+
// --rotate --add-age property. Row reads AMBER on criterion e
|
|
12
|
+
// (unanchored vendor-conformance evidence for ADR-902's default
|
|
13
|
+
// vendor sops+age).
|
|
14
|
+
// accountBound: false.
|
|
15
|
+
|
|
16
|
+
import { writeFile, readFile } from 'node:fs/promises';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { execFileSync } from 'node:child_process';
|
|
19
|
+
import { createScratchAgeScope, DECLARED_ENV } from './probe-utils.mjs';
|
|
20
|
+
import { runSops, readSopsMetadata } from '../../../../packages/rcf-lite/test/fixtures/security-secrets-management/src/sops-cli.mjs';
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
// Probes pass an explicit minimal env to sops children, never a
|
|
24
|
+
// spread of the entire ambient process.env. Only SOPS_AGE_KEY_FILE,
|
|
25
|
+
// PATH and HOME are forwarded; the fixture README's env-var table
|
|
26
|
+
// is the declared surface.
|
|
27
|
+
function sopsEnv(keyPath) {
|
|
28
|
+
return {
|
|
29
|
+
SOPS_AGE_KEY_FILE: keyPath,
|
|
30
|
+
PATH: process.env.PATH || '',
|
|
31
|
+
HOME: process.env.HOME || '',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const CONFORMANCE_LIM = "security-secrets-management-AC-8102-1: probe drives the SOPS binary at the crypto layer; the AC states the project exposes one Secrets Manager module (get, getRequired, list, refresh) that is the only importer of the vendor binding, which requires observing the deployed manager through the integration harness follow-up.";
|
|
35
|
+
export const anchorAcId = null; // No AC/REQ observes SOPS-native rotation.
|
|
36
|
+
export const capability = 'secretsProvider';
|
|
37
|
+
export const accountBound = false;
|
|
38
|
+
|
|
39
|
+
export default async function runProbe() {
|
|
40
|
+
const scopeA = await createScratchAgeScope({ prefix: 'pxe-secrets-a-' });
|
|
41
|
+
const scopeB = await createScratchAgeScope({ prefix: 'pxe-secrets-b-' });
|
|
42
|
+
const evidence = { envDeclared: [...DECLARED_ENV], recipientA: scopeA.recipient, recipientB: scopeB.recipient };
|
|
43
|
+
const results = [];
|
|
44
|
+
try {
|
|
45
|
+
const cipherPath = join(scopeA.dir, 'scope.enc.json');
|
|
46
|
+
const plaintext = JSON.stringify({ v: 'rotation-probe' }, null, 2) + '\n';
|
|
47
|
+
await writeFile(join(scopeA.dir, 'scope.json'), plaintext, 'utf8');
|
|
48
|
+
|
|
49
|
+
// Encrypt against recipient A.
|
|
50
|
+
const enc = runSops(
|
|
51
|
+
['--age', scopeA.recipient, '--encrypt', '--output', cipherPath, join(scopeA.dir, 'scope.json')],
|
|
52
|
+
{ env: sopsEnv(scopeA.keyPath) },
|
|
53
|
+
);
|
|
54
|
+
if (enc.status !== 0) {
|
|
55
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM, anchorAcId, capability, verdict: 'fail', detail: `SOPS-native initial encrypt failed (no AC anchor) status=${enc.status} stderr=${enc.stderr.slice(0, 200)}` });
|
|
56
|
+
return { results, extra: evidence };
|
|
57
|
+
}
|
|
58
|
+
const before = readSopsMetadata(await readFile(cipherPath, 'utf8'));
|
|
59
|
+
evidence.metaBefore = before;
|
|
60
|
+
|
|
61
|
+
// Rotate: add recipient B in-place. sops --rotate --in-place --add-age <B>
|
|
62
|
+
const rot = runSops(
|
|
63
|
+
['--rotate', '--in-place', '--add-age', scopeB.recipient, cipherPath],
|
|
64
|
+
{ env: sopsEnv(scopeA.keyPath) },
|
|
65
|
+
);
|
|
66
|
+
if (rot.status !== 0) {
|
|
67
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM, anchorAcId, capability, verdict: 'fail', detail: `SOPS-native --rotate failed (no AC anchor) status=${rot.status} stderr=${rot.stderr.slice(0, 200)}` });
|
|
68
|
+
return { results, extra: evidence };
|
|
69
|
+
}
|
|
70
|
+
const after = readSopsMetadata(await readFile(cipherPath, 'utf8'));
|
|
71
|
+
evidence.metaAfter = after;
|
|
72
|
+
|
|
73
|
+
// Assertion 1: recipient list grew and now contains B.
|
|
74
|
+
const bothRecipients = after.recipients.includes(scopeA.recipient) && after.recipients.includes(scopeB.recipient);
|
|
75
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM,
|
|
76
|
+
anchorAcId,
|
|
77
|
+
capability,
|
|
78
|
+
verdict: bothRecipients ? 'pass' : 'fail',
|
|
79
|
+
detail: `SOPS-native --rotate --add-age observation (vendor-conformance for ADR-902 sops+age; no AC/REQ anchor; row AMBER). recipients after rotation: ${JSON.stringify(after.recipients)}; bothPresent=${bothRecipients}`,
|
|
80
|
+
evidence: { before: before.recipients, after: after.recipients },
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Assertion 2: mac changed (data key re-wrapped => the payload's MAC is regenerated).
|
|
84
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM,
|
|
85
|
+
anchorAcId: null,
|
|
86
|
+
capability,
|
|
87
|
+
verdict: after.mac !== before.mac ? 'pass' : 'fail',
|
|
88
|
+
detail: `SOPS-native MAC divergence observation (vendor-conformance for ADR-902 sops+age; no AC/REQ anchor; row AMBER). mac before=${before.mac && before.mac.slice(0, 16)}... after=${after.mac && after.mac.slice(0, 16)}... diverged=${after.mac !== before.mac}`,
|
|
89
|
+
evidence: { macDiverged: after.mac !== before.mac },
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Assertion 3: recipient B can decrypt with only its key.
|
|
93
|
+
const decB = runSops(['--decrypt', cipherPath], { env: sopsEnv(scopeB.keyPath) });
|
|
94
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM,
|
|
95
|
+
anchorAcId: null,
|
|
96
|
+
capability,
|
|
97
|
+
verdict: decB.status === 0 && JSON.stringify(JSON.parse(decB.stdout)) === JSON.stringify(JSON.parse(plaintext)) ? 'pass' : 'fail',
|
|
98
|
+
detail: `SOPS-native new-recipient decrypt observation (vendor-conformance for ADR-902 sops+age; no AC/REQ anchor; row AMBER). recipient B decrypt status=${decB.status} matchesPlaintext=${JSON.stringify(JSON.parse(decB.stdout)) === JSON.stringify(JSON.parse(plaintext))}`,
|
|
99
|
+
evidence: { statusB: decB.status, stdoutLen: decB.stdout.length },
|
|
100
|
+
});
|
|
101
|
+
} finally {
|
|
102
|
+
await scopeA.cleanup();
|
|
103
|
+
await scopeB.cleanup();
|
|
104
|
+
evidence.cleanedUp = true;
|
|
105
|
+
}
|
|
106
|
+
return { results, extra: evidence };
|
|
107
|
+
}
|
package/blueprints/security-secrets-management/contributions/probes/encrypt-decrypt-round-trip.mjs
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Encrypt / decrypt round-trip probe for security-secrets-management.
|
|
2
|
+
// Generates a throwaway age keypair in the operator's scratchpad,
|
|
3
|
+
// encrypts a scratch scope file with sops (binary mode so bytes are
|
|
4
|
+
// preserved verbatim), records the sops.mac + sops.lastmodified
|
|
5
|
+
// metadata, decrypts back to the original bytes, and asserts BYTE
|
|
6
|
+
// equality (not JSON equality) between the original file and the
|
|
7
|
+
// decrypted stream. Positive evidence per rule 7d shape 2: the sops
|
|
8
|
+
// metadata excerpt (mac, lastmodified, recipient list) captured
|
|
9
|
+
// from the ciphertext.
|
|
10
|
+
//
|
|
11
|
+
// capability: secretsProvider.
|
|
12
|
+
// Anchor honesty. No AC or REQ observes the SOPS-native
|
|
13
|
+
// byte-equality property this probe evidences, so the probe is
|
|
14
|
+
// left unanchored (anchorAcId=null) and records the evidence as
|
|
15
|
+
// vendor-conformance for ADR-902's default vendor (sops+age).
|
|
16
|
+
// The slug reads AMBER on criterion e until a manager-client
|
|
17
|
+
// probe is added that observes AC-8102-1 at the module boundary.
|
|
18
|
+
// accountBound: false (real sops+age engine on this machine).
|
|
19
|
+
|
|
20
|
+
import { writeFile, readFile } from 'node:fs/promises';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
23
|
+
import { Buffer } from 'node:buffer';
|
|
24
|
+
import { createScratchAgeScope, DECLARED_ENV } from './probe-utils.mjs';
|
|
25
|
+
import { runSops, readSopsMetadata } from '../../../../packages/rcf-lite/test/fixtures/security-secrets-management/src/sops-cli.mjs';
|
|
26
|
+
|
|
27
|
+
const CONFORMANCE_LIM = "security-secrets-management-AC-8102-1: probe drives the SOPS binary at the crypto layer; the AC states the project exposes one Secrets Manager module (get, getRequired, list, refresh) that is the only importer of the vendor binding, which requires observing the deployed manager through the integration harness follow-up.";
|
|
28
|
+
export const anchorAcId = null; // No AC or REQ observes the SOPS-native encrypt/decrypt/rotation/mismatched-key property; see probe file comment.
|
|
29
|
+
export const capability = 'secretsProvider';
|
|
30
|
+
export const accountBound = false;
|
|
31
|
+
|
|
32
|
+
// Probes pass an explicit minimal env to sops children, never a
|
|
33
|
+
// spread of the entire ambient process.env. Only SOPS_AGE_KEY_FILE,
|
|
34
|
+
// PATH and HOME are forwarded; the fixture README's env-var table
|
|
35
|
+
// is the declared surface.
|
|
36
|
+
function sopsEnv(keyPath) {
|
|
37
|
+
return {
|
|
38
|
+
SOPS_AGE_KEY_FILE: keyPath,
|
|
39
|
+
PATH: process.env.PATH || '',
|
|
40
|
+
HOME: process.env.HOME || '',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default async function runProbe() {
|
|
45
|
+
const scope = await createScratchAgeScope();
|
|
46
|
+
const evidence = { envDeclared: [...DECLARED_ENV], scopeDir: scope.dir, recipient: scope.recipient };
|
|
47
|
+
const results = [];
|
|
48
|
+
try {
|
|
49
|
+
const plainPath = join(scope.dir, 'scope.bin');
|
|
50
|
+
const cipherPath = join(scope.dir, 'scope.enc.json');
|
|
51
|
+
// Use raw random bytes plus a marker so a real byte compare is
|
|
52
|
+
// meaningful (JSON round-tripping reformats and defeats byte
|
|
53
|
+
// equality; --input-type binary preserves the exact stream).
|
|
54
|
+
const plaintextBytes = Buffer.concat([Buffer.from('pxe-secrets:'), randomBytes(256)]);
|
|
55
|
+
await writeFile(plainPath, plaintextBytes);
|
|
56
|
+
|
|
57
|
+
// Encrypt with sops --input-type binary --output-type binary
|
|
58
|
+
// against the throwaway age recipient.
|
|
59
|
+
const encRes = runSops(
|
|
60
|
+
['--age', scope.recipient, '--input-type', 'binary', '--output-type', 'json', '--encrypt', '--output', cipherPath, plainPath],
|
|
61
|
+
{ env: sopsEnv(scope.keyPath) },
|
|
62
|
+
);
|
|
63
|
+
if (encRes.status !== 0) {
|
|
64
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM,
|
|
65
|
+
anchorAcId, capability, verdict: 'fail',
|
|
66
|
+
detail: `SOPS-native encrypt failed (vendor-conformance evidence for ADR-902 sops+age; no AC/REQ anchor) status=${encRes.status} stderr=${encRes.stderr.slice(0, 200)}`,
|
|
67
|
+
evidence: { encStatus: encRes.status, encStderr: encRes.stderr.slice(0, 200) },
|
|
68
|
+
});
|
|
69
|
+
return { results, extra: evidence };
|
|
70
|
+
}
|
|
71
|
+
const cipherText = await readFile(cipherPath, 'utf8');
|
|
72
|
+
const cipherMeta = readSopsMetadata(cipherText);
|
|
73
|
+
evidence.cipherMeta = cipherMeta;
|
|
74
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM,
|
|
75
|
+
anchorAcId,
|
|
76
|
+
capability,
|
|
77
|
+
verdict: cipherMeta.mac && cipherMeta.lastmodified && cipherMeta.recipients.includes(scope.recipient) ? 'pass' : 'fail',
|
|
78
|
+
detail: `SOPS-native ciphertext-metadata observation (vendor-conformance evidence for ADR-902's default vendor sops+age; NO AC or REQ states the SOPS-native property this probe observes; slug reads AMBER on criterion e until a manager-client probe is added that observes the Secrets Manager module (get, getRequired, list, refresh) at its own boundary (AC-8102-1)). mac=${cipherMeta.mac ? cipherMeta.mac.slice(0, 40) : null} lastmodified=${cipherMeta.lastmodified} recipients=${JSON.stringify(cipherMeta.recipients)}`,
|
|
79
|
+
evidence: { sopsMetadata: cipherMeta, notObservableACsOrREQs: 'SOPS-native crypto layer; slug is AMBER on criterion-e until a manager-client probe is added that observes the Secrets Manager module (get, getRequired, list, refresh) at its boundary.' },
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Decrypt with sops --output <file> so binary bytes survive the
|
|
83
|
+
// spawnSync string-encoded stdout channel (utf8 replacement would
|
|
84
|
+
// otherwise corrupt any non-utf8 byte before the compare).
|
|
85
|
+
const decryptedPath = join(scope.dir, 'scope.dec.bin');
|
|
86
|
+
const decRes = runSops(
|
|
87
|
+
['--decrypt', '--input-type', 'json', '--output-type', 'binary', '--output', decryptedPath, cipherPath],
|
|
88
|
+
{ env: sopsEnv(scope.keyPath) },
|
|
89
|
+
);
|
|
90
|
+
if (decRes.status !== 0) {
|
|
91
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM,
|
|
92
|
+
anchorAcId, capability, verdict: 'fail',
|
|
93
|
+
detail: `SOPS-native decrypt failed (vendor-conformance evidence for ADR-902 sops+age; no AC/REQ anchor) status=${decRes.status} stderr=${decRes.stderr.slice(0, 200)}`,
|
|
94
|
+
evidence: { decStatus: decRes.status, decStderr: decRes.stderr.slice(0, 200) },
|
|
95
|
+
});
|
|
96
|
+
return { results, extra: evidence };
|
|
97
|
+
}
|
|
98
|
+
const decryptedBytes = await readFile(decryptedPath);
|
|
99
|
+
const originalBytes = plaintextBytes;
|
|
100
|
+
const originalHash = createHash('sha256').update(originalBytes).digest('hex');
|
|
101
|
+
const decryptedHash = createHash('sha256').update(decryptedBytes).digest('hex');
|
|
102
|
+
const bytesEqual = originalBytes.length === decryptedBytes.length && Buffer.compare(originalBytes, decryptedBytes) === 0;
|
|
103
|
+
evidence.byteCompare = {
|
|
104
|
+
originalLength: originalBytes.length,
|
|
105
|
+
decryptedLength: decryptedBytes.length,
|
|
106
|
+
originalSha256: originalHash,
|
|
107
|
+
decryptedSha256: decryptedHash,
|
|
108
|
+
bytesEqual,
|
|
109
|
+
};
|
|
110
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM,
|
|
111
|
+
anchorAcId,
|
|
112
|
+
capability,
|
|
113
|
+
verdict: bytesEqual ? 'pass' : 'fail',
|
|
114
|
+
detail: `SOPS-native byte-equality on the encrypt/decrypt round-trip (Buffer.compare === 0 on the raw bytes; vendor-conformance evidence for ADR-902 sops+age; no AC or REQ anchors this property; row reads AMBER). originalLength=${originalBytes.length} decryptedLength=${decryptedBytes.length} sha256(original)=${originalHash.slice(0, 16)} sha256(decrypted)=${decryptedHash.slice(0, 16)} bufferCompareZero=${bytesEqual}`,
|
|
115
|
+
evidence: { ...evidence.byteCompare, notObservableACsOrREQs: 'SOPS-native crypto layer; slug is AMBER on criterion-e until a manager-client probe is added.' },
|
|
116
|
+
});
|
|
117
|
+
} finally {
|
|
118
|
+
await scope.cleanup();
|
|
119
|
+
evidence.cleanedUp = true;
|
|
120
|
+
}
|
|
121
|
+
return { results, extra: evidence };
|
|
122
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Key-rotation probe. Encrypts a scratch scope with age recipient
|
|
2
|
+
// A; runs sops --rotate --in-place with the same recipient list;
|
|
3
|
+
// asserts the payload mac changed (new data key => new MAC) while
|
|
4
|
+
// the recipient list is unchanged. Proves the sops rotation verb
|
|
5
|
+
// re-keys the payload without touching the recipient set.
|
|
6
|
+
//
|
|
7
|
+
// capability: secretsProvider.
|
|
8
|
+
// Anchor honesty. No AC or REQ observes the SOPS-native --rotate
|
|
9
|
+
// property. Row reads AMBER on criterion e (unanchored
|
|
10
|
+
// vendor-conformance evidence for ADR-902's default vendor sops+age)
|
|
11
|
+
// until a manager-client probe is added that observes the Secrets Manager module (get, getRequired, list, refresh) at
|
|
12
|
+
// its own boundary (AC-8102-1).
|
|
13
|
+
// accountBound: false.
|
|
14
|
+
|
|
15
|
+
import { writeFile, readFile } from 'node:fs/promises';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { createScratchAgeScope, DECLARED_ENV } from './probe-utils.mjs';
|
|
18
|
+
import { runSops, readSopsMetadata } from '../../../../packages/rcf-lite/test/fixtures/security-secrets-management/src/sops-cli.mjs';
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
// Probes pass an explicit minimal env to sops children, never a
|
|
22
|
+
// spread of the entire ambient process.env. Only SOPS_AGE_KEY_FILE,
|
|
23
|
+
// PATH and HOME are forwarded; the fixture README's env-var table
|
|
24
|
+
// is the declared surface.
|
|
25
|
+
function sopsEnv(keyPath) {
|
|
26
|
+
return {
|
|
27
|
+
SOPS_AGE_KEY_FILE: keyPath,
|
|
28
|
+
PATH: process.env.PATH || '',
|
|
29
|
+
HOME: process.env.HOME || '',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const CONFORMANCE_LIM = "security-secrets-management-AC-8102-1: probe drives the SOPS binary at the crypto layer; the AC states the project exposes one Secrets Manager module (get, getRequired, list, refresh) that is the only importer of the vendor binding, which requires observing the deployed manager through the integration harness follow-up.";
|
|
33
|
+
export const anchorAcId = null; // No AC/REQ observes SOPS-native rotation; see probe file header.
|
|
34
|
+
export const capability = 'secretsProvider';
|
|
35
|
+
export const accountBound = false;
|
|
36
|
+
|
|
37
|
+
export default async function runProbe() {
|
|
38
|
+
const scope = await createScratchAgeScope({ prefix: 'pxe-secrets-rot-' });
|
|
39
|
+
const evidence = { envDeclared: [...DECLARED_ENV], recipient: scope.recipient };
|
|
40
|
+
const results = [];
|
|
41
|
+
try {
|
|
42
|
+
const cipherPath = join(scope.dir, 'scope.enc.json');
|
|
43
|
+
const plaintext = JSON.stringify({ v: 'rotate-only' }, null, 2) + '\n';
|
|
44
|
+
await writeFile(join(scope.dir, 'scope.json'), plaintext, 'utf8');
|
|
45
|
+
const enc = runSops(['--age', scope.recipient, '--encrypt', '--output', cipherPath, join(scope.dir, 'scope.json')],
|
|
46
|
+
{ env: sopsEnv(scope.keyPath) });
|
|
47
|
+
if (enc.status !== 0) {
|
|
48
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM, anchorAcId, capability, verdict: 'fail', detail: `SOPS-native initial encrypt failed (no AC anchor) status=${enc.status} stderr=${enc.stderr.slice(0, 200)}` });
|
|
49
|
+
return { results, extra: evidence };
|
|
50
|
+
}
|
|
51
|
+
const before = readSopsMetadata(await readFile(cipherPath, 'utf8'));
|
|
52
|
+
const rot = runSops(['--rotate', '--in-place', cipherPath], { env: sopsEnv(scope.keyPath) });
|
|
53
|
+
if (rot.status !== 0) {
|
|
54
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM, anchorAcId, capability, verdict: 'fail', detail: `SOPS-native --rotate failed (no AC anchor) status=${rot.status} stderr=${rot.stderr.slice(0, 200)}` });
|
|
55
|
+
return { results, extra: evidence };
|
|
56
|
+
}
|
|
57
|
+
const after = readSopsMetadata(await readFile(cipherPath, 'utf8'));
|
|
58
|
+
evidence.metaBefore = before; evidence.metaAfter = after;
|
|
59
|
+
|
|
60
|
+
// Recipient list unchanged.
|
|
61
|
+
const sameRecipients = JSON.stringify(before.recipients) === JSON.stringify(after.recipients);
|
|
62
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM,
|
|
63
|
+
anchorAcId,
|
|
64
|
+
capability,
|
|
65
|
+
verdict: sameRecipients ? 'pass' : 'fail',
|
|
66
|
+
detail: `SOPS-native --rotate observation (vendor-conformance for ADR-902 sops+age; no AC/REQ anchor; row AMBER). recipients unchanged across --rotate: before=${JSON.stringify(before.recipients)} after=${JSON.stringify(after.recipients)}`,
|
|
67
|
+
evidence: { sameRecipients },
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// MAC changed (new data key).
|
|
71
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM,
|
|
72
|
+
anchorAcId: null,
|
|
73
|
+
capability,
|
|
74
|
+
verdict: after.mac !== before.mac ? 'pass' : 'fail',
|
|
75
|
+
detail: `SOPS-native --rotate MAC divergence observation (vendor-conformance for ADR-902 sops+age; no AC/REQ anchor; row AMBER). mac diverged after --rotate: before=${before.mac.slice(0, 64)} after=${after.mac.slice(0, 64)}...`,
|
|
76
|
+
evidence: { macBefore: before.mac, macAfter: after.mac },
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Decryption still works with the same key.
|
|
80
|
+
const dec = runSops(['--decrypt', cipherPath], { env: sopsEnv(scope.keyPath) });
|
|
81
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM,
|
|
82
|
+
anchorAcId: null,
|
|
83
|
+
capability,
|
|
84
|
+
verdict: dec.status === 0 && JSON.stringify(JSON.parse(dec.stdout)) === JSON.stringify(JSON.parse(plaintext)) ? 'pass' : 'fail',
|
|
85
|
+
detail: `SOPS-native post-rotation decrypt observation (vendor-conformance for ADR-902 sops+age; no AC/REQ anchor; row AMBER). post-rotation decrypt: status=${dec.status} matchesPlaintext=${JSON.stringify(JSON.parse(dec.stdout)) === JSON.stringify(JSON.parse(plaintext))}`,
|
|
86
|
+
evidence: { status: dec.status, matched: JSON.stringify(JSON.parse(dec.stdout)) === JSON.stringify(JSON.parse(plaintext)) },
|
|
87
|
+
});
|
|
88
|
+
} finally {
|
|
89
|
+
await scope.cleanup();
|
|
90
|
+
evidence.cleanedUp = true;
|
|
91
|
+
}
|
|
92
|
+
return { results, extra: evidence };
|
|
93
|
+
}
|
package/blueprints/security-secrets-management/contributions/probes/mismatched-key-refusal.mjs
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Mismatched-key refusal probe. Encrypts a scratch scope with age
|
|
2
|
+
// recipient A; attempts to decrypt with age recipient B's key
|
|
3
|
+
// only; asserts sops refuses (non-zero exit, no plaintext on
|
|
4
|
+
// stdout). Proves the shipped adapter fails closed on a wrong
|
|
5
|
+
// key rather than degrading to unencrypted.
|
|
6
|
+
//
|
|
7
|
+
// capability: secretsProvider.
|
|
8
|
+
// Anchor honesty. No AC or REQ observes the SOPS-native
|
|
9
|
+
// mismatched-key refusal property. Row reads AMBER on criterion e
|
|
10
|
+
// (unanchored vendor-conformance evidence for ADR-902's default
|
|
11
|
+
// vendor sops+age).
|
|
12
|
+
// accountBound: false.
|
|
13
|
+
|
|
14
|
+
import { writeFile } from 'node:fs/promises';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { createScratchAgeScope, DECLARED_ENV } from './probe-utils.mjs';
|
|
17
|
+
import { runSops } from '../../../../packages/rcf-lite/test/fixtures/security-secrets-management/src/sops-cli.mjs';
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
// Probes pass an explicit minimal env to sops children, never a
|
|
21
|
+
// spread of the entire ambient process.env. Only SOPS_AGE_KEY_FILE,
|
|
22
|
+
// PATH and HOME are forwarded; the fixture README's env-var table
|
|
23
|
+
// is the declared surface.
|
|
24
|
+
function sopsEnv(keyPath) {
|
|
25
|
+
return {
|
|
26
|
+
SOPS_AGE_KEY_FILE: keyPath,
|
|
27
|
+
PATH: process.env.PATH || '',
|
|
28
|
+
HOME: process.env.HOME || '',
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const CONFORMANCE_LIM = "security-secrets-management-AC-8102-1: probe drives the SOPS binary at the crypto layer; the AC states the project exposes one Secrets Manager module (get, getRequired, list, refresh) that is the only importer of the vendor binding, which requires observing the deployed manager through the integration harness follow-up.";
|
|
32
|
+
export const anchorAcId = null; // No AC/REQ observes SOPS-native mismatched-key refusal.
|
|
33
|
+
export const capability = 'secretsProvider';
|
|
34
|
+
export const accountBound = false;
|
|
35
|
+
|
|
36
|
+
export default async function runProbe() {
|
|
37
|
+
const scopeA = await createScratchAgeScope({ prefix: 'pxe-secrets-mm-a-' });
|
|
38
|
+
const scopeB = await createScratchAgeScope({ prefix: 'pxe-secrets-mm-b-' });
|
|
39
|
+
const evidence = { envDeclared: [...DECLARED_ENV], recipientA: scopeA.recipient, recipientB: scopeB.recipient };
|
|
40
|
+
const results = [];
|
|
41
|
+
try {
|
|
42
|
+
const cipherPath = join(scopeA.dir, 'scope.enc.json');
|
|
43
|
+
const plaintext = JSON.stringify({ v: 'mismatch-probe' }, null, 2) + '\n';
|
|
44
|
+
await writeFile(join(scopeA.dir, 'scope.json'), plaintext, 'utf8');
|
|
45
|
+
runSops(['--age', scopeA.recipient, '--encrypt', '--output', cipherPath, join(scopeA.dir, 'scope.json')],
|
|
46
|
+
{ env: sopsEnv(scopeA.keyPath) });
|
|
47
|
+
// Attempt decrypt with B only.
|
|
48
|
+
const badDec = runSops(['--decrypt', cipherPath], { env: sopsEnv(scopeB.keyPath) });
|
|
49
|
+
evidence.badStatus = badDec.status;
|
|
50
|
+
evidence.badStderrExcerpt = (badDec.stderr || '').slice(0, 200);
|
|
51
|
+
const refused = badDec.status !== 0 && !badDec.stdout.includes('mismatch-probe');
|
|
52
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM,
|
|
53
|
+
anchorAcId,
|
|
54
|
+
capability,
|
|
55
|
+
verdict: refused ? 'pass' : 'fail',
|
|
56
|
+
detail: `SOPS-native mismatched-key refusal observation (vendor-conformance for ADR-902 sops+age; no AC/REQ anchor; row AMBER). mismatched-key decrypt refused: exitStatus=${badDec.status} stdoutContainsPlain=${badDec.stdout.includes('mismatch-probe')} stderrExcerpt=${(badDec.stderr || '').slice(0, 200)}`,
|
|
57
|
+
evidence: { exitStatus: badDec.status, refused },
|
|
58
|
+
});
|
|
59
|
+
// Sanity: A can still decrypt.
|
|
60
|
+
const goodDec = runSops(['--decrypt', cipherPath], { env: sopsEnv(scopeA.keyPath) });
|
|
61
|
+
results.push({ conformanceOnly: true, limitation: CONFORMANCE_LIM,
|
|
62
|
+
anchorAcId: null,
|
|
63
|
+
capability,
|
|
64
|
+
verdict: goodDec.status === 0 && JSON.stringify(JSON.parse(goodDec.stdout)) === JSON.stringify(JSON.parse(plaintext)) ? 'pass' : 'fail',
|
|
65
|
+
detail: `SOPS-native correct-key sanity (vendor-conformance for ADR-902 sops+age; no AC/REQ anchor; row AMBER). correct-key decrypt sanity: status=${goodDec.status} matched=${JSON.stringify(JSON.parse(goodDec.stdout)) === JSON.stringify(JSON.parse(plaintext))}`,
|
|
66
|
+
evidence: { status: goodDec.status },
|
|
67
|
+
});
|
|
68
|
+
} finally {
|
|
69
|
+
await scopeA.cleanup();
|
|
70
|
+
await scopeB.cleanup();
|
|
71
|
+
evidence.cleanedUp = true;
|
|
72
|
+
}
|
|
73
|
+
return { results, extra: evidence };
|
|
74
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// Shared helpers for security-secrets-management probes.
|
|
2
|
+
//
|
|
3
|
+
// Engine posture: sops (v3.x) + age (v1.x) are real, in-process
|
|
4
|
+
// engines on the developer machine. Every probe runs against a
|
|
5
|
+
// throwaway age recipient the probe itself generates in a scratch
|
|
6
|
+
// directory under the operator's scratchpad, encrypts a scratch
|
|
7
|
+
// scope file, and cleans up on completion. The estate's canonical
|
|
8
|
+
// vault (repo-root .vault/) is NEVER touched.
|
|
9
|
+
//
|
|
10
|
+
// SOPS metadata (lastmodified, mac, unencrypted_suffix, and the
|
|
11
|
+
// recipients array under sops.age[]) is inspected as positive
|
|
12
|
+
// evidence per rule 7d shape 2 (response-body excerpt): a before/
|
|
13
|
+
// after MAC divergence proves the encrypt/decrypt cycle mutated
|
|
14
|
+
// the file; a recipient added to sops.age[] proves the rotation
|
|
15
|
+
// verb succeeded.
|
|
16
|
+
|
|
17
|
+
import { mkdir, writeFile, mkdtemp, rm } from 'node:fs/promises';
|
|
18
|
+
import { dirname, resolve, join } from 'node:path';
|
|
19
|
+
import { fileURLToPath } from 'node:url';
|
|
20
|
+
import { tmpdir } from 'node:os';
|
|
21
|
+
|
|
22
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
export const PROJECT_ROOT = resolve(HERE, '..', '..', '..', '..');
|
|
24
|
+
export const SLUG = 'security-secrets-management';
|
|
25
|
+
export const FIXTURE_DIR = resolve(PROJECT_ROOT, 'packages/rcf-lite/test/fixtures/security-secrets-management');
|
|
26
|
+
export const REPORT_DIR = resolve(PROJECT_ROOT, '.rcf/reports/blueprints/security-secrets-management');
|
|
27
|
+
|
|
28
|
+
export const DECLARED_ENV = Object.freeze([
|
|
29
|
+
'SOPS_AGE_KEY_FILE',
|
|
30
|
+
'RCF_SECRETS_SCRATCH_DIR',
|
|
31
|
+
'PATH',
|
|
32
|
+
'HOME',
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
// First-8-words-of-anchor helper. Each result row's `detail` starts
|
|
36
|
+
// with the first eight words of the anchored AC's or REQ's
|
|
37
|
+
// description text. Anchor lookup walks contributions/user-stories/
|
|
38
|
+
// and contributions/requirements/ under the blueprint root and
|
|
39
|
+
// caches the map on first call. Null / unknown / missing anchors
|
|
40
|
+
// yield an empty prefix.
|
|
41
|
+
import { readFileSync as _rf } from 'node:fs';
|
|
42
|
+
import { readdirSync as _rd } from 'node:fs';
|
|
43
|
+
let _anchorMap = null;
|
|
44
|
+
function _loadAnchorMap() {
|
|
45
|
+
const map = new Map();
|
|
46
|
+
const dirs = [
|
|
47
|
+
{ dir: resolve(PROJECT_ROOT, 'blueprints', SLUG, 'contributions', 'user-stories'), kind: 'ac' },
|
|
48
|
+
{ dir: resolve(PROJECT_ROOT, 'blueprints', SLUG, 'contributions', 'requirements'), kind: 'req' },
|
|
49
|
+
];
|
|
50
|
+
for (const { dir, kind } of dirs) {
|
|
51
|
+
let entries;
|
|
52
|
+
try { entries = _rd(dir); } catch { continue; }
|
|
53
|
+
for (const name of entries) {
|
|
54
|
+
if (!name.endsWith('.json')) continue;
|
|
55
|
+
let j;
|
|
56
|
+
try { j = JSON.parse(_rf(resolve(dir, name), 'utf8')); } catch { continue; }
|
|
57
|
+
if (kind === 'ac') {
|
|
58
|
+
const usId = j.usId;
|
|
59
|
+
for (const ac of (j.acceptanceCriteria || [])) {
|
|
60
|
+
// AC id in the shipped JSON is like "AC-9101-1"; the
|
|
61
|
+
// anchor form used by probes is "<slug>-AC-9101-1".
|
|
62
|
+
const key = `${SLUG}-${ac.id}`;
|
|
63
|
+
map.set(key, ac.description || '');
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
const key = j.reqId; // already "<slug>-REQ-###"
|
|
67
|
+
if (key) map.set(key, j.description || j.title || '');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return map;
|
|
72
|
+
}
|
|
73
|
+
export function first8Words(anchor) {
|
|
74
|
+
if (!anchor) return '';
|
|
75
|
+
if (!_anchorMap) _anchorMap = _loadAnchorMap();
|
|
76
|
+
const txt = _anchorMap.get(anchor) || '';
|
|
77
|
+
if (!txt) return '';
|
|
78
|
+
const words = txt.split(/\s+/).filter(Boolean).slice(0, 8).join(' ');
|
|
79
|
+
return words;
|
|
80
|
+
}
|
|
81
|
+
export function withAnchorPrefix(row) {
|
|
82
|
+
const prefix = first8Words(row.anchorAcId);
|
|
83
|
+
if (!prefix) return row;
|
|
84
|
+
const already = typeof row.detail === 'string' && row.detail.startsWith(prefix);
|
|
85
|
+
if (already) return row;
|
|
86
|
+
const oldDetail = row.detail || '';
|
|
87
|
+
return { ...row, detail: `${prefix} :: ${oldDetail}` };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function aggregate(results) {
|
|
91
|
+
if (!Array.isArray(results) || results.length === 0) return 'fail';
|
|
92
|
+
if (results.some((r) => r.verdict === 'fail')) return 'fail';
|
|
93
|
+
if (results.some((r) => r.verdict === 'warn')) return 'warn';
|
|
94
|
+
return 'pass';
|
|
95
|
+
}
|
|
96
|
+
export function isSkipped(results) {
|
|
97
|
+
return results.length > 0 && results.every((r) => r.accountBoundSkipped === true);
|
|
98
|
+
}
|
|
99
|
+
export async function writeReport({ probeName, engine, results, extra }) {
|
|
100
|
+
await mkdir(REPORT_DIR, { recursive: true });
|
|
101
|
+
// Prefix every row's detail with the first eight words of its
|
|
102
|
+
// anchored AC or REQ description; unanchored rows are left alone.
|
|
103
|
+
const prefixedResults = (results || []).map(withAnchorPrefix);
|
|
104
|
+
const raw = aggregate(prefixedResults);
|
|
105
|
+
const aggregateVerdict = isSkipped(prefixedResults) ? 'pass' : raw;
|
|
106
|
+
const report = { slug: 'security-secrets-management', probeName, runAt: new Date().toISOString(), engine, results: prefixedResults, aggregateVerdict, ...(extra ?? {}) };
|
|
107
|
+
const path = resolve(REPORT_DIR, `${probeName}.json`);
|
|
108
|
+
await writeFile(path, JSON.stringify(report, null, 2) + '\n', 'utf8');
|
|
109
|
+
return { report, path };
|
|
110
|
+
}
|
|
111
|
+
export async function runShim(probeName, engine, mainFn) {
|
|
112
|
+
try {
|
|
113
|
+
const outcome = await mainFn();
|
|
114
|
+
if (!outcome || !Array.isArray(outcome.results) || outcome.results.length === 0) {
|
|
115
|
+
const results = [{
|
|
116
|
+
anchorAcId: 'unknown',
|
|
117
|
+
verdict: 'fail',
|
|
118
|
+
detail: 'no checks ran',
|
|
119
|
+
}];
|
|
120
|
+
const { report, path } = await writeReport({ probeName, engine, results });
|
|
121
|
+
process.stdout.write(JSON.stringify(report, null, 2) + '\n');
|
|
122
|
+
process.stderr.write(`no-checks-ran fail written to ${path}\n`);
|
|
123
|
+
process.exitCode = 1;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const { results, extra } = outcome;
|
|
127
|
+
const { report, path } = await writeReport({ probeName, engine, results, extra });
|
|
128
|
+
process.stdout.write(JSON.stringify(report, null, 2) + '\n');
|
|
129
|
+
process.stdout.write(`report written to ${path}\n`);
|
|
130
|
+
if (report.aggregateVerdict === 'fail') process.exitCode = 1;
|
|
131
|
+
} catch (err) {
|
|
132
|
+
const results = [{ anchorAcId: 'unknown', verdict: 'fail', detail: `probe threw: ${err && err.message ? err.message : String(err)}` }];
|
|
133
|
+
const { report, path } = await writeReport({ probeName, engine, results });
|
|
134
|
+
process.stdout.write(JSON.stringify(report, null, 2) + '\n');
|
|
135
|
+
process.stderr.write(`probe error: ${err && err.stack ? err.stack : String(err)}\n`);
|
|
136
|
+
process.stderr.write(`report written to ${path}\n`);
|
|
137
|
+
process.exitCode = 1;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Creates a scratch working dir under RCF_SECRETS_SCRATCH_DIR (or
|
|
142
|
+
// the OS tmpdir), generates a fresh throwaway age keypair inside
|
|
143
|
+
// it and writes the key to a file the caller can point sops at.
|
|
144
|
+
// Returns { dir, keyPath, recipient } and a cleanup function.
|
|
145
|
+
import { execFileSync } from 'node:child_process';
|
|
146
|
+
|
|
147
|
+
// Engine-absent detection. The security-secrets-management probes
|
|
148
|
+
// drive real age + sops binaries in-process; when either binary is
|
|
149
|
+
// missing on the host (a stock Ubuntu runner without the install
|
|
150
|
+
// step; a bare macOS box) the honest posture is a skip that names
|
|
151
|
+
// the missing engine, not an ENOENT throw from execFileSync. The
|
|
152
|
+
// anatomy test consumes this reason and records a skip row shaped
|
|
153
|
+
// `{ notObservableHere: { ac, reason } }` rather than failing.
|
|
154
|
+
const ENGINE_ABSENT_REASON = 'age or sops binary absent on this host';
|
|
155
|
+
const REQUIRED_ENGINE_BINARIES = Object.freeze(['age-keygen', 'sops']);
|
|
156
|
+
export function engineAbsentReason() {
|
|
157
|
+
for (const bin of REQUIRED_ENGINE_BINARIES) {
|
|
158
|
+
try { execFileSync(bin, ['--version'], { stdio: 'ignore' }); }
|
|
159
|
+
catch { return ENGINE_ABSENT_REASON; }
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
export class EngineAbsentError extends Error {
|
|
164
|
+
constructor() {
|
|
165
|
+
super(ENGINE_ABSENT_REASON);
|
|
166
|
+
this.name = 'EngineAbsentError';
|
|
167
|
+
this.engineAbsent = true;
|
|
168
|
+
this.reason = ENGINE_ABSENT_REASON;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// De-claim helper. When the shipped AC does not observe what the probe observes, the row keeps its
|
|
173
|
+
// verdict, evidence and engine as they are, but the anchor drops to null and the
|
|
174
|
+
// row carries conformanceOnly:true plus a limitation string naming the AC that
|
|
175
|
+
// this probe does NOT observe and why. The integration-harness follow-up (the auth integration harness follow-up)
|
|
176
|
+
// is the surface where those AC-level properties become observable.
|
|
177
|
+
export function deClaim(row, { ac, limitation }) {
|
|
178
|
+
const acId = ac || 'unknown-AC';
|
|
179
|
+
const limText = (limitation || '').trim();
|
|
180
|
+
const finalLim = /^(security-[a-z0-9-]+-)?(AC|REQ)-/.test(limText) ? limText : (limText ? `${acId}: ${limText}` : `${acId}: not observable at this probe surface; needs the integration harness (the auth integration harness follow-up)`);
|
|
181
|
+
const out = { ...row };
|
|
182
|
+
out.anchorAcId = null;
|
|
183
|
+
if ('anchorReqId' in out) delete out.anchorReqId;
|
|
184
|
+
out.conformanceOnly = true;
|
|
185
|
+
out.limitation = finalLim;
|
|
186
|
+
const tag = ` :: conformance-only (${finalLim})`;
|
|
187
|
+
if (typeof out.detail === 'string' && !out.detail.includes('conformance-only')) {
|
|
188
|
+
out.detail = out.detail + tag;
|
|
189
|
+
}
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export async function createScratchAgeScope({ prefix = 'pxe-secrets-' } = {}) {
|
|
194
|
+
// Engine-absent probes must never reach execFileSync('age-keygen')
|
|
195
|
+
// and dump an ENOENT stack; the anatomy test's honest posture is a
|
|
196
|
+
// typed EngineAbsentError the caller can route to a skip row.
|
|
197
|
+
const absent = engineAbsentReason();
|
|
198
|
+
if (absent) throw new EngineAbsentError();
|
|
199
|
+
const base = process.env.RCF_SECRETS_SCRATCH_DIR || tmpdir();
|
|
200
|
+
await mkdir(base, { recursive: true });
|
|
201
|
+
const dir = await mkdtemp(join(base, prefix));
|
|
202
|
+
const keyPath = join(dir, 'age-key.txt');
|
|
203
|
+
// age-keygen -o <path> writes both public and private key.
|
|
204
|
+
execFileSync('age-keygen', ['-o', keyPath], { stdio: 'pipe' });
|
|
205
|
+
// Read the public key line: `# public key: age1...`
|
|
206
|
+
const keyText = execFileSync('cat', [keyPath], { encoding: 'utf8' });
|
|
207
|
+
const m = keyText.match(/# public key:\s*(age1[a-z0-9]+)/i);
|
|
208
|
+
if (!m) throw new Error('age-keygen output missing public key comment');
|
|
209
|
+
const recipient = m[1];
|
|
210
|
+
return {
|
|
211
|
+
dir, keyPath, recipient,
|
|
212
|
+
async cleanup() { await rm(dir, { recursive: true, force: true }); },
|
|
213
|
+
};
|
|
214
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import runProbe from './encrypt-decrypt-round-trip.mjs';
|
|
2
|
+
import { runShim } from './probe-utils.mjs';
|
|
3
|
+
const engine = { kind: 'sops+age', image: 'sops(1) + age(1) via throwaway keys in scratchpad', healthy: true };
|
|
4
|
+
runShim('encrypt-decrypt-round-trip', engine, runProbe);
|