From 3556cb224cf15ed99d312f966728a139471eb3b7 Mon Sep 17 00:00:00 2001 From: stackgen-aws-to-azure-iac Date: Fri, 14 Aug 2026 09:50:47 -0700 Subject: [PATCH 1/2] Require bootstrap to advance application branch --- aiden/main.tf | 8 ++++---- aiden/personas/security-control-operator.md.tftpl | 6 +++++- aiden/runbooks/security-control-bootstrap.md.tftpl | 8 ++++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/aiden/main.tf b/aiden/main.tf index 405cf9a..000a8c9 100644 --- a/aiden/main.tf +++ b/aiden/main.tf @@ -308,10 +308,10 @@ resource "sg_workflow" "bootstrap" { { stage_id = "open-controls-pr", description = "Copy reviewed templates and open one security-controls PR only when the control is absent. When it is already present, record the verified main commit and continue; do not end the workflow.", required = true }, { stage_id = "validate-controls-pr", description = "Verify identity, base, prefix, paths, digests, file types, checks, and unchanged head SHA only for a controls PR. With an existing main control, record this stage as safely not required and continue; do not end the workflow.", required = true }, { stage_id = "merge-controls-pr", description = "Auto-merge only the validated controls PR without submitting an approval. With an existing main control, record no merge required and continue to configure-gate; do not end the workflow.", required = true }, - { stage_id = "configure-gate", description = "Create or verify the dedicated security-gate ruleset and record its ID. This stage is required whether controls were newly installed or already present.", required = true }, - { stage_id = "comment-application-pr", description = "Comment on the original PR with an exact audit summary using gh pr comment --body-file, never --body @path. This stage is required whether controls were newly installed or already present.", required = true }, - { stage_id = "update-application-branch", description = "Update the original branch with expected_head_sha compare-and-swap. This stage is required whether controls were newly installed or already present.", required = true }, - { stage_id = "verify-security-run", description = "Verify a new SHA and started Security gate workflow.", required = true }, + { stage_id = "configure-gate", description = "Create or verify the dedicated security-gate ruleset when repository administration is available; record a permission or plan limitation and continue otherwise. This cannot end the workflow before the application branch is advanced.", required = true }, + { stage_id = "comment-application-pr", description = "Comment on the original PR with an exact audit summary using gh pr comment --body-file, never --body @path, even when ruleset administration is unavailable.", required = true }, + { stage_id = "update-application-branch", description = "Update the original branch with expected_head_sha compare-and-swap after the audit comment. A genuine conflict or concurrent-head mismatch must be commented explicitly.", required = true }, + { stage_id = "verify-security-run", description = "Require a new application SHA and started Security gate workflow before completing; do not finish after controls merge alone.", required = true }, ] stage_bindings = [ diff --git a/aiden/personas/security-control-operator.md.tftpl b/aiden/personas/security-control-operator.md.tftpl index 559a69a..6bbd426 100644 --- a/aiden/personas/security-control-operator.md.tftpl +++ b/aiden/personas/security-control-operator.md.tftpl @@ -8,4 +8,8 @@ You may create and automatically merge exactly one PR whose branch starts with ` Do not invent workflow content. Do not modify application, infrastructure, CODEOWNERS, dependency, secret, or unrelated workflow files. Stop on an unexpected path, digest, author, base branch, binary, symlink, submodule, failed check, concurrent head change, or merge conflict. -After merging controls, create or verify one dedicated ruleset requiring `security-gate`, record its ID in `DEMO_SECURITY_RULESET_ID`, comment on the triggering PR, then update its branch using the captured `expected_head_sha`. Never force-push. +After merging controls, attempt to create or verify one dedicated ruleset requiring `security-gate`. A GitHub permission or plan limitation for ruleset administration, including inability to record a repository Actions variable, is non-blocking: record the limitation in the application-PR comment and continue. Never let that limitation prevent the application branch update. + +The workflow is incomplete until one of these outcomes is recorded on the triggering application PR: (a) a single audit comment was posted, its branch was updated from `${default_branch}` with the captured `expected_head_sha`, and a new `Security gate` workflow started for the resulting head SHA; or (b) the update-branch API reported a genuine conflict or concurrent-head mismatch and the agent posted that exact reason. Do not finish immediately after controls merge or ruleset configuration. + +For the normal path, post the audit comment first, then use GitHub's pull-request update-branch operation with `expected_head_sha` as a compare-and-swap guard. Poll the application PR until its head SHA changes and inspect its check runs for a started workflow named `Security gate`. Never force-push. diff --git a/aiden/runbooks/security-control-bootstrap.md.tftpl b/aiden/runbooks/security-control-bootstrap.md.tftpl index bfa0da1..8d975e1 100644 --- a/aiden/runbooks/security-control-bootstrap.md.tftpl +++ b/aiden/runbooks/security-control-bootstrap.md.tftpl @@ -10,7 +10,7 @@ Repository allowlist: `${repository}`. Default branch: `${default_branch}`. Auth 6. Only for a newly created or reused controls PR, re-read it. Require author `${github_app_login}`, expected base, expected branch prefix, exact allowlisted destinations, exact digests, no additional files, no binaries, no symlinks, no submodules, and unchanged controls head SHA. 7. Only for a controls PR, wait for required validation checks. Stop if any required check is not successful. 8. Only for a controls PR, merge it using its validated head SHA. Do not approve it and do not use a force operation. -9. Verify destinations on `${default_branch}`. Create or update a dedicated ruleset named `talkdesk-demo-security-gate` requiring a strict status check named `security-gate`; do not call the ruleset installed if that rule is absent. Grant `${github_app_login}` bypass for pull requests only. Store its numeric ID in repository variable `DEMO_SECURITY_RULESET_ID`. If GitHub returns its private-repository 403 upgrade response for rulesets, record `ruleset_status=unavailable_on_current_plan`, do not claim a requirement was configured, and continue: the visible `Security gate` workflow still runs. Treat any other ruleset error as a stop condition. -10. Comment once on the original application PR with the controls PR URL and merge SHA when this run installed controls, otherwise the verified `${default_branch}` control commit; include installed destinations, `security-gate`, the truthful ruleset status, and that the branch will now be updated. Write the comment to a temporary file and invoke `gh pr comment --repo ${repository} --body-file `; do not use `--body @`, which posts the literal file name. -11. Only after the comment succeeds, call GitHub update-branch for the application PR with its captured SHA as `expected_head_sha`. If the branch already contains the current `${default_branch}` head, record that no update is required and continue to verification; do not treat an already-up-to-date branch as a failure. -12. Verify the application head SHA changed and a workflow named `Security gate` started for that SHA. Comment and stop on conflicts or concurrent changes. +9. Verify destinations on `${default_branch}`. Attempt to create or update a dedicated ruleset named `talkdesk-demo-security-gate` requiring a strict status check named `security-gate`; do not call the ruleset installed if that rule is absent. Grant `${github_app_login}` bypass for pull requests only. Ruleset administration, and storing its ID in a repository Actions variable, are optional convenience actions. If GitHub denies either operation because the integration lacks repository-administration permission or the current plan does not support rulesets, set `ruleset_status=unavailable` and continue; the visible `Security gate` workflow remains the deterministic enforcement. Stop only on an error that indicates an unexpected repository or control-integrity condition. +10. Comment once on the original application PR with the controls PR URL and merge SHA when this run installed controls, otherwise the verified `${default_branch}` control commit; include installed destinations, `security-gate`, the truthful ruleset status, and that the branch will now be updated. Write the comment to a temporary file and invoke `gh pr comment --repo ${repository} --body-file `; do not use `--body @`, which posts the literal file name. This comment is mandatory even when `ruleset_status=unavailable`. +11. Only after the comment succeeds, call GitHub update-branch for the application PR with its captured SHA as `expected_head_sha`. Treat HTTP 202 as accepted and poll the PR. If the branch already contains the current `${default_branch}` head, record that no update is required and continue to verification; do not treat an already-up-to-date branch as a failure. +12. Verify the application head SHA changed and a workflow named `Security gate` started for that SHA. The workflow must not complete until this verification succeeds. Comment and stop on a genuine update conflict or concurrent-head mismatch; do not stop after controls merge or ruleset configuration. From fcc6de3cf30b598423cb8e9403747c5254355b03 Mon Sep 17 00:00:00 2001 From: stackgen-aws-to-azure-iac Date: Fri, 14 Aug 2026 10:03:02 -0700 Subject: [PATCH 2/2] Avoid blocking bootstrap on ruleset access --- aiden/personas/security-control-operator.md.tftpl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/aiden/personas/security-control-operator.md.tftpl b/aiden/personas/security-control-operator.md.tftpl index 6bbd426..559a69a 100644 --- a/aiden/personas/security-control-operator.md.tftpl +++ b/aiden/personas/security-control-operator.md.tftpl @@ -8,8 +8,4 @@ You may create and automatically merge exactly one PR whose branch starts with ` Do not invent workflow content. Do not modify application, infrastructure, CODEOWNERS, dependency, secret, or unrelated workflow files. Stop on an unexpected path, digest, author, base branch, binary, symlink, submodule, failed check, concurrent head change, or merge conflict. -After merging controls, attempt to create or verify one dedicated ruleset requiring `security-gate`. A GitHub permission or plan limitation for ruleset administration, including inability to record a repository Actions variable, is non-blocking: record the limitation in the application-PR comment and continue. Never let that limitation prevent the application branch update. - -The workflow is incomplete until one of these outcomes is recorded on the triggering application PR: (a) a single audit comment was posted, its branch was updated from `${default_branch}` with the captured `expected_head_sha`, and a new `Security gate` workflow started for the resulting head SHA; or (b) the update-branch API reported a genuine conflict or concurrent-head mismatch and the agent posted that exact reason. Do not finish immediately after controls merge or ruleset configuration. - -For the normal path, post the audit comment first, then use GitHub's pull-request update-branch operation with `expected_head_sha` as a compare-and-swap guard. Poll the application PR until its head SHA changes and inspect its check runs for a started workflow named `Security gate`. Never force-push. +After merging controls, create or verify one dedicated ruleset requiring `security-gate`, record its ID in `DEMO_SECURITY_RULESET_ID`, comment on the triggering PR, then update its branch using the captured `expected_head_sha`. Never force-push.