diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json index cea16445e6a..c675b847054 100644 --- a/.github/aw/actions-lock.json +++ b/.github/aw/actions-lock.json @@ -1,13 +1,11 @@ { "entries": { "actions-ecosystem/action-add-labels@v1.1.3": { - "repo": "actions-ecosystem/action-add-labels", - "version": "v1.1.3", - "sha": "c96b68fec76a0987cd93957189e9abd0b9a72ff1", + "action_description": "Add labels to an issue or a pull request.", "inputs": { "github_token": { - "description": "A GitHub token.", - "default": "${{ github.token }}" + "default": "${{ github.token }}", + "description": "A GitHub token." }, "labels": { "description": "The labels' name to be added. Must be separated with line breaks if there're multiple labels.", @@ -17,269 +15,271 @@ "description": "The number of the issue or pull request." }, "repo": { - "description": "The owner and repository name. e.g.) Codertocat/Hello-World", - "default": "${{ github.repository }}" + "default": "${{ github.repository }}", + "description": "The owner and repository name. e.g.) Codertocat/Hello-World" } }, - "action_description": "Add labels to an issue or a pull request." + "repo": "actions-ecosystem/action-add-labels", + "sha": "c96b68fec76a0987cd93957189e9abd0b9a72ff1", + "version": "v1.1.3" }, "actions/cache/restore@v6.1.0": { "repo": "actions/cache/restore", - "version": "v6.1.0", - "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9" + "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9", + "version": "v6.1.0" }, "actions/cache/save@v6.1.0": { "repo": "actions/cache/save", - "version": "v6.1.0", - "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9" + "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9", + "version": "v6.1.0" }, "actions/cache@v6.1.0": { "repo": "actions/cache", - "version": "v6.1.0", - "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9" + "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9", + "version": "v6.1.0" }, "actions/checkout@v7.0.0": { "repo": "actions/checkout", - "version": "v7.0.0", - "sha": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" + "sha": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "version": "v7.0.0" }, "actions/create-github-app-token@v3.2.0": { "repo": "actions/create-github-app-token", - "version": "v3.2.0", - "sha": "bcd2ba49218906704ab6c1aa796996da409d3eb1" + "sha": "bcd2ba49218906704ab6c1aa796996da409d3eb1", + "version": "v3.2.0" }, "actions/download-artifact@v8.0.1": { "repo": "actions/download-artifact", - "version": "v8.0.1", - "sha": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c" + "sha": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", + "version": "v8.0.1" }, "actions/github-script@v9.0.0": { "repo": "actions/github-script", - "version": "v9.0.0", - "sha": "3a2844b7e9c422d3c10d287c895573f7108da1b3" + "sha": "3a2844b7e9c422d3c10d287c895573f7108da1b3", + "version": "v9.0.0" }, "actions/setup-dotnet@v5.4.0": { "repo": "actions/setup-dotnet", - "version": "v5.4.0", - "sha": "26b0ec14cb23fa6904739307f278c14f94c95bf1" + "sha": "26b0ec14cb23fa6904739307f278c14f94c95bf1", + "version": "v5.4.0" }, "actions/setup-go@v6.5.0": { "repo": "actions/setup-go", - "version": "v6.5.0", - "sha": "924ae3a1cded613372ab5595356fb5720e22ba16" + "sha": "924ae3a1cded613372ab5595356fb5720e22ba16", + "version": "v6.5.0" }, "actions/setup-java@v5.4.0": { "repo": "actions/setup-java", - "version": "v5.4.0", - "sha": "1bcf9fb12cf4aa7d266a90ae39939e61372fe520" + "sha": "1bcf9fb12cf4aa7d266a90ae39939e61372fe520", + "version": "v5.4.0" }, "actions/setup-node@v6.4.0": { "repo": "actions/setup-node", - "version": "v6.4.0", - "sha": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e" + "sha": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e", + "version": "v6.4.0" }, "actions/setup-python@v6.3.0": { "repo": "actions/setup-python", - "version": "v6.3.0", - "sha": "ece7cb06caefa5fff74198d8649806c4678c61a1" + "sha": "ece7cb06caefa5fff74198d8649806c4678c61a1", + "version": "v6.3.0" }, "actions/upload-artifact@v7.0.1": { "repo": "actions/upload-artifact", - "version": "v7.0.1", - "sha": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" + "sha": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "version": "v7.0.1" }, "anchore/sbom-action@v0.24.0": { "repo": "anchore/sbom-action", - "version": "v0.24.0", - "sha": "e22c389904149dbc22b58101806040fa8d37a610" + "sha": "e22c389904149dbc22b58101806040fa8d37a610", + "version": "v0.24.0" }, "astral-sh/setup-uv@v8.2.0": { "repo": "astral-sh/setup-uv", - "version": "v8.2.0", - "sha": "fac544c07dec837d0ccb6301d7b5580bf5edae39" + "sha": "fac544c07dec837d0ccb6301d7b5580bf5edae39", + "version": "v8.2.0" }, "denoland/setup-deno@v2.0.4": { "repo": "denoland/setup-deno", - "version": "v2.0.4", - "sha": "667a34cdef165d8d2b2e98dde39547c9daac7282" + "sha": "667a34cdef165d8d2b2e98dde39547c9daac7282", + "version": "v2.0.4" }, "docker/build-push-action@v7.2.0": { + "released_at": "2026-05-21T15:23:58Z", "repo": "docker/build-push-action", - "version": "v7.2.0", "sha": "f9f3042f7e2789586610d6e8b85c8f03e5195baf", - "released_at": "2026-05-21T15:23:58Z" + "version": "v7.2.0" }, "docker/login-action@v4.2.0": { + "released_at": "2026-05-22T11:55:00Z", "repo": "docker/login-action", - "version": "v4.2.0", "sha": "650006c6eb7dba73a995cc03b0b2d7f5ca915bee", - "released_at": "2026-05-22T11:55:00Z" + "version": "v4.2.0" }, "docker/metadata-action@v6.1.0": { "repo": "docker/metadata-action", - "version": "v6.1.0", - "sha": "80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9" + "sha": "80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9", + "version": "v6.1.0" }, "docker/setup-buildx-action@v4.1.0": { "repo": "docker/setup-buildx-action", - "version": "v4.1.0", - "sha": "d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5" + "sha": "d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5", + "version": "v4.1.0" }, "erlef/setup-beam@v1.24.0": { "repo": "erlef/setup-beam", - "version": "v1.24.0", - "sha": "fc68ffb90438ef2936bbb3251622353b3dcb2f93" + "sha": "fc68ffb90438ef2936bbb3251622353b3dcb2f93", + "version": "v1.24.0" }, "github/codeql-action/upload-sarif@v4.36.2": { "repo": "github/codeql-action/upload-sarif", - "version": "v4.36.2", - "sha": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e" + "sha": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "version": "v4.36.2" }, "github/stale-repos@v9.0.15": { "repo": "github/stale-repos", - "version": "v9.0.15", - "sha": "a0db8bfe2318ed6ddbd01e95d7633803f99f77e9" + "sha": "a0db8bfe2318ed6ddbd01e95d7633803f99f77e9", + "version": "v9.0.15" }, "haskell-actions/setup@v2.11.0": { "repo": "haskell-actions/setup", - "version": "v2.11.0", - "sha": "cd0d9bdd65b20557f41bea4dbe43d0b5fbbfe553" + "sha": "cd0d9bdd65b20557f41bea4dbe43d0b5fbbfe553", + "version": "v2.11.0" }, "oven-sh/setup-bun@v2.2.0": { "repo": "oven-sh/setup-bun", - "version": "v2.2.0", - "sha": "0c5077e51419868618aeaa5fe8019c62421857d6" + "sha": "0c5077e51419868618aeaa5fe8019c62421857d6", + "version": "v2.2.0" }, "ruby/setup-ruby@v1.314.0": { "repo": "ruby/setup-ruby", - "version": "v1.314.0", - "sha": "9eb537ca036ebaed86729dcb9309076e4c5c3b74" + "sha": "9eb537ca036ebaed86729dcb9309076e4c5c3b74", + "version": "v1.314.0" }, "safedep/pmg@v1": { "repo": "safedep/pmg", - "version": "v1", - "sha": "46cc70db535107183c9e752bb55d1d5c5f1a9290" + "sha": "46cc70db535107183c9e752bb55d1d5c5f1a9290", + "version": "v1" }, "super-linter/super-linter@v8.7.0": { "repo": "super-linter/super-linter", - "version": "v8.7.0", - "sha": "4ce20838b8ab83717e78138c5b3a1407148e0918" + "sha": "4ce20838b8ab83717e78138c5b3a1407148e0918", + "version": "v8.7.0" } }, "containers": { "alpine:latest": { - "image": "alpine:latest", "digest": "sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11", + "image": "alpine:latest", "pinned_image": "alpine:latest@sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11" }, "docker.io/mcp/brave-search": { - "image": "docker.io/mcp/brave-search", "digest": "sha256:ca96b8acb27d8cf601a8faef86a084602cffa41d8cb18caa1e29ba4d16989d22", + "image": "docker.io/mcp/brave-search", "pinned_image": "docker.io/mcp/brave-search@sha256:ca96b8acb27d8cf601a8faef86a084602cffa41d8cb18caa1e29ba4d16989d22" }, "ghcr.io/chopratejas/headroom:latest": { - "image": "ghcr.io/chopratejas/headroom:latest", "digest": "sha256:af709363c4f9515a88a50939baec513be13c7cd778fb6635527b104d5173cb1e", + "image": "ghcr.io/chopratejas/headroom:latest", "pinned_image": "ghcr.io/chopratejas/headroom:latest@sha256:af709363c4f9515a88a50939baec513be13c7cd778fb6635527b104d5173cb1e" }, "ghcr.io/github/gh-aw-firewall/agent-act:0.25.29": { - "image": "ghcr.io/github/gh-aw-firewall/agent-act:0.25.29", "digest": "sha256:97b4cc14dc2123a45b9d5b9927489f66882dec5857de6afc0e5bab257be92ef1", + "image": "ghcr.io/github/gh-aw-firewall/agent-act:0.25.29", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent-act:0.25.29@sha256:97b4cc14dc2123a45b9d5b9927489f66882dec5857de6afc0e5bab257be92ef1" }, "ghcr.io/github/gh-aw-firewall/agent-act:0.27.13": { - "image": "ghcr.io/github/gh-aw-firewall/agent-act:0.27.13", "digest": "sha256:676de58a0abad8054cd9d4fc45b861715856819f083a268372d60bca0090018c", + "image": "ghcr.io/github/gh-aw-firewall/agent-act:0.27.13", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent-act:0.27.13@sha256:676de58a0abad8054cd9d4fc45b861715856819f083a268372d60bca0090018c" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.18": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.18", "digest": "sha256:c77e8c26bab6c39e8568d8e2f8c17015944849a8cbcdfb4bd9725d8893725ca2", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.18", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.18@sha256:c77e8c26bab6c39e8568d8e2f8c17015944849a8cbcdfb4bd9725d8893725ca2" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.20": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.20", "digest": "sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.20", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.20@sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.28": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.28", "digest": "sha256:a8834e285807654bf680154faa710d43fe4365a0868142f5c20e48c85e137a7a", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.28@sha256:a8834e285807654bf680154faa710d43fe4365a0868142f5c20e48c85e137a7a" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.29": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.29", "digest": "sha256:e68f37e36962dcb3f3d1de680a49bc2302cefd001b941a7dc377155ec7ce42f4", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.29", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.29@sha256:e68f37e36962dcb3f3d1de680a49bc2302cefd001b941a7dc377155ec7ce42f4" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.40": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.40", "digest": "sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.40", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.41": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.41", "digest": "sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.41", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.41@sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.66": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.66", "digest": "sha256:8373ba626ffd28ce1af503a3218205fa7744a5774479173f92e50a378a1ed00d", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.66", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.66@sha256:8373ba626ffd28ce1af503a3218205fa7744a5774479173f92e50a378a1ed00d" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.67": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.67", "digest": "sha256:0f54fa48dd1a03ef6d171574eecf9a9edbf0406cea011a534cd12ed1fcb46715", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.67", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.67@sha256:0f54fa48dd1a03ef6d171574eecf9a9edbf0406cea011a534cd12ed1fcb46715" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.0": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.0", "digest": "sha256:3816d1692e6d96887b27f1e4f1d64b8d7edb43ed9d7506b8f203913cbb81c248", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.0", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.0@sha256:3816d1692e6d96887b27f1e4f1d64b8d7edb43ed9d7506b8f203913cbb81c248" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.1": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.1", "digest": "sha256:55149fa2daf8fa8afa2803f2ac1a3534591a7c96f173ee2aec9545fbe67305df", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.1", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.1@sha256:55149fa2daf8fa8afa2803f2ac1a3534591a7c96f173ee2aec9545fbe67305df" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.11": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.11", "digest": "sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.11", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.12": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.12", "digest": "sha256:a3911ae633bfc49a96be10e660d447b65023731850fee3de8a074ff8861ffde3", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.12", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.12@sha256:a3911ae633bfc49a96be10e660d447b65023731850fee3de8a074ff8861ffde3" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.13": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.13", "digest": "sha256:691a06b64961b5b35aac117eaace202fa721e91da19d1d2e22dcdd6663cd571b", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.13", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.13@sha256:691a06b64961b5b35aac117eaace202fa721e91da19d1d2e22dcdd6663cd571b" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.2": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.2", "digest": "sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.2", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.22": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.22", "digest": "sha256:55f06588411008b7148eb64b8dfe28602a0cce3675b36c6b190b54aca138468e", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.22", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.22@sha256:55f06588411008b7148eb64b8dfe28602a0cce3675b36c6b190b54aca138468e" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.26": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.26", "digest": "sha256:b283b1f037d2e068532fe178a06f2944696c3933ba11604979134e7896ac6f8c", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.26", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.26@sha256:b283b1f037d2e068532fe178a06f2944696c3933ba11604979134e7896ac6f8c" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.27": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.27", "digest": "sha256:bb5a0150dcff1cddf9b8045bb411b7759806bace0abcb132fb22158073e155d9", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.27", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.27@sha256:bb5a0150dcff1cddf9b8045bb411b7759806bace0abcb132fb22158073e155d9" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.28": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.28", "digest": "sha256:53225868cdf5600c3458bdb122d0a9d2b04a67000476bb338da03718231cd632", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.28@sha256:53225868cdf5600c3458bdb122d0a9d2b04a67000476bb338da03718231cd632" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.29": { @@ -288,113 +288,113 @@ "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.29@sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.4": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.4", "digest": "sha256:b268ebf37df2428b19efcb383f001d65dc6a5ec10af43feb886d1a8477ab0e3a", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.4", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.4@sha256:b268ebf37df2428b19efcb383f001d65dc6a5ec10af43feb886d1a8477ab0e3a" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.6": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.6", "digest": "sha256:5b778c712a25397a38a47cee3467a9cbc726b16320cc133a0758c0592a6f0792", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.6", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.6@sha256:5b778c712a25397a38a47cee3467a9cbc726b16320cc133a0758c0592a6f0792" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.7": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.7", "digest": "sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.7", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.9": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.9", "digest": "sha256:13f522853a688bfe24b04adbbe40b68101e8ef4b6fe0b636068527141bf1c269", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.9", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.9@sha256:13f522853a688bfe24b04adbbe40b68101e8ef4b6fe0b636068527141bf1c269" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.18": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.18", "digest": "sha256:d16a40a3ca6e989896d0cef9f31b9412bb1fcc8755bafcafb95012ae1078539b", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.18", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.18@sha256:d16a40a3ca6e989896d0cef9f31b9412bb1fcc8755bafcafb95012ae1078539b" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20", "digest": "sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20@sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28", "digest": "sha256:93290f2393752252911bd7c39a047f776c0b53063575e7bde4e304962a9a61cb", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28@sha256:93290f2393752252911bd7c39a047f776c0b53063575e7bde4e304962a9a61cb" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.29": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.29", "digest": "sha256:d1219e4110684402aabbeb5a43858f26790c9d0be210581cf3f7a521bd2c87b6", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.29", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.29@sha256:d1219e4110684402aabbeb5a43858f26790c9d0be210581cf3f7a521bd2c87b6" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40", "digest": "sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41", "digest": "sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41@sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.66": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.66", "digest": "sha256:4da87d0c633816cec29a26b1d1ed5fbcb0df38de19075f0429bf1d6deec3dcdc", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.66", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.66@sha256:4da87d0c633816cec29a26b1d1ed5fbcb0df38de19075f0429bf1d6deec3dcdc" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.67": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.67", "digest": "sha256:d3f51df1869bda0e1f71ae31a81450641c6ae67404e0769469aae34c2738aeb5", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.67", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.67@sha256:d3f51df1869bda0e1f71ae31a81450641c6ae67404e0769469aae34c2738aeb5" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.0": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.0", "digest": "sha256:f28d2bd3197fb6ef9ec40ef345bbf2bb33e50151a8e72e89abb618fc3d0066eb", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.0", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.0@sha256:f28d2bd3197fb6ef9ec40ef345bbf2bb33e50151a8e72e89abb618fc3d0066eb" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.1": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.1", "digest": "sha256:2802437f05830336ea3ae8639f628776608d14d95b5b3cf30f161eb505e29752", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.1", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.1@sha256:2802437f05830336ea3ae8639f628776608d14d95b5b3cf30f161eb505e29752" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11", "digest": "sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.12": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.12", "digest": "sha256:2329aff5e54bb5bf466ad94182eebbdfe4a4db497a9dfbe87442eb3400aa4a86", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.12", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.12@sha256:2329aff5e54bb5bf466ad94182eebbdfe4a4db497a9dfbe87442eb3400aa4a86" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.13": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.13", "digest": "sha256:c57febf4aeeefbb4fd96f5b12c07f4279ca55edca6a700032debf9dd0787286e", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.13", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.13@sha256:c57febf4aeeefbb4fd96f5b12c07f4279ca55edca6a700032debf9dd0787286e" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2", "digest": "sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.22": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.22", "digest": "sha256:afb9ff9140b17d38871dfb9dbac5ff8689ea634c2f91c435da2825192d4881c1", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.22", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.22@sha256:afb9ff9140b17d38871dfb9dbac5ff8689ea634c2f91c435da2825192d4881c1" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26", "digest": "sha256:1743dbac9bf4225f3acfdcbce4f77f5a3e61e22b2e929305525f00196693c015", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26@sha256:1743dbac9bf4225f3acfdcbce4f77f5a3e61e22b2e929305525f00196693c015" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.27": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.27", "digest": "sha256:01e58c4383fa9952abe76e0a134a27c970f81f744d6b7861fc9e08b7964d94c3", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.27", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.27@sha256:01e58c4383fa9952abe76e0a134a27c970f81f744d6b7861fc9e08b7964d94c3" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.28": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.28", "digest": "sha256:9c409216ac1c130d2d94758586a3b9a7847dfa9606a4291354a12486e9fd370a", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.28@sha256:9c409216ac1c130d2d94758586a3b9a7847dfa9606a4291354a12486e9fd370a" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29": { @@ -403,38 +403,38 @@ "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29@sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.4": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.4", "digest": "sha256:3ea0d12a2d124db8ed6e2d18aff040e30ab3568161f258a132fccdeede4198cd", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.4", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.4@sha256:3ea0d12a2d124db8ed6e2d18aff040e30ab3568161f258a132fccdeede4198cd" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.6": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.6", "digest": "sha256:7b14e481f3a9898f1e9be50acc4e58541d9fcd85b49b1e4945b708f1bf1bf68e", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.6", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.6@sha256:7b14e481f3a9898f1e9be50acc4e58541d9fcd85b49b1e4945b708f1bf1bf68e" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7", "digest": "sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.9": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.9", "digest": "sha256:9fec93937dc9d3e04f3954705c2c42f58976ebb8479b10778602631b5316e1e2", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.9", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.9@sha256:9fec93937dc9d3e04f3954705c2c42f58976ebb8479b10778602631b5316e1e2" }, "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26": { - "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26", "digest": "sha256:5823f6cec65210cd6e3e6320c165979fb46f78c76600f58f4189d2c89c2be8da", + "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26", "pinned_image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26@sha256:5823f6cec65210cd6e3e6320c165979fb46f78c76600f58f4189d2c89c2be8da" }, "ghcr.io/github/gh-aw-firewall/build-tools:0.27.27": { - "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.27", "digest": "sha256:d767c00ef264ad894c73e2db192581f4a93a3297fb134aeac3e48657bb23507e", + "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.27", "pinned_image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.27@sha256:d767c00ef264ad894c73e2db192581f4a93a3297fb134aeac3e48657bb23507e" }, "ghcr.io/github/gh-aw-firewall/build-tools:0.27.28": { - "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.28", "digest": "sha256:4f9827958511e680d6a4b37910da35c154606f7efccf85c7efd76dbeb22481a2", + "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.28@sha256:4f9827958511e680d6a4b37910da35c154606f7efccf85c7efd76dbeb22481a2" }, "ghcr.io/github/gh-aw-firewall/build-tools:0.27.29": { @@ -443,78 +443,78 @@ "pinned_image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.29@sha256:fbedcc57550fcbb0ad386fda8028ad77172ee505f53af9a8d8a7b8e1644b0e38" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28", "digest": "sha256:fdf310e4678ce58d248c466b89399e9680a3003038fd19322c388559016aaac7", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28@sha256:fdf310e4678ce58d248c466b89399e9680a3003038fd19322c388559016aaac7" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.29": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.29", "digest": "sha256:29917488eb90a01ff9544ffeeb5cc26434a8ea16d69ae8972f5f6be0e567e276", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.29", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.29@sha256:29917488eb90a01ff9544ffeeb5cc26434a8ea16d69ae8972f5f6be0e567e276" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.40": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.40", "digest": "sha256:3e7152911d4b4b7b97beef9d3d7d924ff7902227e86001ef3838fb728d5d514c", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.40", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.40@sha256:3e7152911d4b4b7b97beef9d3d7d924ff7902227e86001ef3838fb728d5d514c" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.41": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.41", "digest": "sha256:62171f2fa508667b8b0a9e096f826983f312e3da0ce894f80c0f83a875af60fe", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.41", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.41@sha256:62171f2fa508667b8b0a9e096f826983f312e3da0ce894f80c0f83a875af60fe" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.66": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.66", "digest": "sha256:ff53f2b227ceaf7334baeab5daa2537211089aca251af9630cb0b5a8899492d0", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.66", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.66@sha256:ff53f2b227ceaf7334baeab5daa2537211089aca251af9630cb0b5a8899492d0" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.67": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.67", "digest": "sha256:97387002ec54c8ab9f255d5aaf3d435071642e058f528c8268ca0e80b201609a", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.67", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.67@sha256:97387002ec54c8ab9f255d5aaf3d435071642e058f528c8268ca0e80b201609a" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.0": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.0", "digest": "sha256:42529ecb9f90da5adb00593d268dfdbd35d14bb1dc92dd897286b27ce1e3d58d", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.0", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.0@sha256:42529ecb9f90da5adb00593d268dfdbd35d14bb1dc92dd897286b27ce1e3d58d" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.1": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.1", "digest": "sha256:2e6dc98321dbf82840f83ec0ef8b198506149255a15d3a7854d59c0d34063e27", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.1", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.1@sha256:2e6dc98321dbf82840f83ec0ef8b198506149255a15d3a7854d59c0d34063e27" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.12": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.12", "digest": "sha256:7d267199839cc0f23c01fd39f8c050a5d86cb6cc5058aa829cd472f405fb1a1d", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.12", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.12@sha256:7d267199839cc0f23c01fd39f8c050a5d86cb6cc5058aa829cd472f405fb1a1d" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.13": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.13", "digest": "sha256:79dfd3a5d139bd1956ba6d7d1782b831a07175cf5afa29c45cb20bb0140f23c5", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.13", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.13@sha256:79dfd3a5d139bd1956ba6d7d1782b831a07175cf5afa29c45cb20bb0140f23c5" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.2": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.2", "digest": "sha256:02f3ec08f32dc26c5427920c6a2e2f3036238fce44802f2f11ef49ed8621b5d0", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.2", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.2@sha256:02f3ec08f32dc26c5427920c6a2e2f3036238fce44802f2f11ef49ed8621b5d0" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.22": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.22", "digest": "sha256:e23e1604241f579b418e6522d938285b57ada31bc27742a65c90ee2250b1755c", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.22", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.22@sha256:e23e1604241f579b418e6522d938285b57ada31bc27742a65c90ee2250b1755c" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26", "digest": "sha256:6006b1c579ca550e023697b5dfd832ae03361328a4c0f2eb49fb181a6b8d7a4b", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26@sha256:6006b1c579ca550e023697b5dfd832ae03361328a4c0f2eb49fb181a6b8d7a4b" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.27": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.27", "digest": "sha256:70df326caf73bf5911340dca4620b529a483dd8f42142b0a41d7b9761ab4ab7a", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.27", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.27@sha256:70df326caf73bf5911340dca4620b529a483dd8f42142b0a41d7b9761ab4ab7a" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.28": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.28", "digest": "sha256:65f9b00a2dfa9a093a3e65de8259ba37d962b12ce1dfec15670f005961cddad4", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.28@sha256:65f9b00a2dfa9a093a3e65de8259ba37d962b12ce1dfec15670f005961cddad4" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29": { @@ -523,113 +523,113 @@ "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29@sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.4": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.4", "digest": "sha256:72c378c029d2fad4684847ab44c329e526ac6b1a78cdf97656870ea11d201545", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.4", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.4@sha256:72c378c029d2fad4684847ab44c329e526ac6b1a78cdf97656870ea11d201545" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.6": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.6", "digest": "sha256:194b21f5d3284b0b2abf2603a14ec607f89d798165a7ef453667706c69401735", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.6", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.6@sha256:194b21f5d3284b0b2abf2603a14ec607f89d798165a7ef453667706c69401735" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7", "digest": "sha256:4757f198a3fa20f88bdbe70be7ae1a05f127d9c0a9e96a5d6460ef40c08fc83d", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7@sha256:4757f198a3fa20f88bdbe70be7ae1a05f127d9c0a9e96a5d6460ef40c08fc83d" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.9": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.9", "digest": "sha256:89b715de241408e03d35d5c4851361ae350f69aa56d7955dd2ed1d6d3ce037cc", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.9", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.9@sha256:89b715de241408e03d35d5c4851361ae350f69aa56d7955dd2ed1d6d3ce037cc" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.18": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.18", "digest": "sha256:eb102afcfbae26ffcec016adebb74d3be7b0a5bf376ba306599cdf3effbe288e", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.18", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.18@sha256:eb102afcfbae26ffcec016adebb74d3be7b0a5bf376ba306599cdf3effbe288e" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.20": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.20", "digest": "sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.20", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.20@sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.28": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.28", "digest": "sha256:844c18280f82cd1b06345eb2f4e91966b34185bfc51c9f237c3e022e848fb474", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.28@sha256:844c18280f82cd1b06345eb2f4e91966b34185bfc51c9f237c3e022e848fb474" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.29": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.29", "digest": "sha256:8a71ad9e40454051672312917e51567abfb8251d7c294d086c48f63d84e4cb53", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.29", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.29@sha256:8a71ad9e40454051672312917e51567abfb8251d7c294d086c48f63d84e4cb53" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.40": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.40", "digest": "sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.40", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.41": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.41", "digest": "sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.41", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.41@sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.66": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.66", "digest": "sha256:9c47e6d0210c5b1581b56d1fe8ac32c1d8fdd7cb04ef95592f2431e59f533bd3", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.66", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.66@sha256:9c47e6d0210c5b1581b56d1fe8ac32c1d8fdd7cb04ef95592f2431e59f533bd3" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.67": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.67", "digest": "sha256:9a05085db054f41bd67c772bcfc25cabc15bc33ee993b051a31e30669dd2031f", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.67", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.67@sha256:9a05085db054f41bd67c772bcfc25cabc15bc33ee993b051a31e30669dd2031f" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.0": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.0", "digest": "sha256:d6a01d4cf3d928e6a7fc42e34afef228e753dce87646edc91d8a5cd0b612d9a6", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.0", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.0@sha256:d6a01d4cf3d928e6a7fc42e34afef228e753dce87646edc91d8a5cd0b612d9a6" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.1": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.1", "digest": "sha256:1f3df3207dc9faa9080088115ca50a5ab0d7a692c61dffa8c8898d0b7b750413", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.1", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.1@sha256:1f3df3207dc9faa9080088115ca50a5ab0d7a692c61dffa8c8898d0b7b750413" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.11": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.11", "digest": "sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.11", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.12": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.12", "digest": "sha256:f5d9403364543b23a8510278854cf5517345419daed19de044a7130c03388484", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.12", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.12@sha256:f5d9403364543b23a8510278854cf5517345419daed19de044a7130c03388484" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.13": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.13", "digest": "sha256:700b1b5a73098373b04fb684f291e95d9be0124ab559717b04f27acaf8b41bed", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.13", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.13@sha256:700b1b5a73098373b04fb684f291e95d9be0124ab559717b04f27acaf8b41bed" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.2": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.2", "digest": "sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.2", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.22": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.22", "digest": "sha256:3cdcc1e2b4b4fe602ba69fd3e21aac7ac512d5c1fce24df4ce69dc4f98164b59", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.22", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.22@sha256:3cdcc1e2b4b4fe602ba69fd3e21aac7ac512d5c1fce24df4ce69dc4f98164b59" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.26": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.26", "digest": "sha256:d4647c3bfaf80889eb1dbd3d4e2063340cbf94c0ca6c5747bbfc8507b12f3485", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.26", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.26@sha256:d4647c3bfaf80889eb1dbd3d4e2063340cbf94c0ca6c5747bbfc8507b12f3485" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.27": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.27", "digest": "sha256:92d820df47b2eff75d93a5bec4dc183a3ec55ed7ddb4f25cb0fdda5c3e995409", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.27", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.27@sha256:92d820df47b2eff75d93a5bec4dc183a3ec55ed7ddb4f25cb0fdda5c3e995409" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.28": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.28", "digest": "sha256:41d20c773ec155c5ff02435fe048bdca497834f559d45d2126ce1ba8a9c83963", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.28@sha256:41d20c773ec155c5ff02435fe048bdca497834f559d45d2126ce1ba8a9c83963" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.29": { @@ -638,233 +638,233 @@ "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.29@sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.4": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.4", "digest": "sha256:87979038897e40caed22245b64d1daa796390d2dca289b99d3d1174c85740af8", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.4", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.4@sha256:87979038897e40caed22245b64d1daa796390d2dca289b99d3d1174c85740af8" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.6": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.6", "digest": "sha256:730985e67931b9774545bce76b3ac5a354aa1dc11f19ee8f2d9cbf3211d73c3a", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.6", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.6@sha256:730985e67931b9774545bce76b3ac5a354aa1dc11f19ee8f2d9cbf3211d73c3a" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.7": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.7", "digest": "sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.7", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.9": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.9", "digest": "sha256:f186085bd04864e59f5057ff9e6bbd506475275ffe165111517d7d1acdc1de9c", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.9", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.9@sha256:f186085bd04864e59f5057ff9e6bbd506475275ffe165111517d7d1acdc1de9c" }, "ghcr.io/github/gh-aw-mcpg:v0.2.19": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.2.19", "digest": "sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd", + "image": "ghcr.io/github/gh-aw-mcpg:v0.2.19", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.2.19@sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd" }, "ghcr.io/github/gh-aw-mcpg:v0.2.30": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.2.30", "digest": "sha256:e950e6d39f003862d33bfb8d4eb93e242d919cf6ca874b90728e5e0ea7434c6f", + "image": "ghcr.io/github/gh-aw-mcpg:v0.2.30", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.2.30@sha256:e950e6d39f003862d33bfb8d4eb93e242d919cf6ca874b90728e5e0ea7434c6f" }, "ghcr.io/github/gh-aw-mcpg:v0.3.0": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.0", "digest": "sha256:9c2228324fb1f26f39dc9471612e530ae3efc3156dac05efb2e8d212878d454d", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.0", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.0@sha256:9c2228324fb1f26f39dc9471612e530ae3efc3156dac05efb2e8d212878d454d" }, "ghcr.io/github/gh-aw-mcpg:v0.3.1": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.1", "digest": "sha256:287fad0236959f3b3d9936ea1ef8d5b4f135ef2a5f5789713495cbbef191e60c", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.1", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.1@sha256:287fad0236959f3b3d9936ea1ef8d5b4f135ef2a5f5789713495cbbef191e60c" }, "ghcr.io/github/gh-aw-mcpg:v0.3.16": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.16", "digest": "sha256:8001e4bfa52d45abd05c45a8f855ce62bc893eb66e4807bb487bf2ff07fc1473", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.16", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.16@sha256:8001e4bfa52d45abd05c45a8f855ce62bc893eb66e4807bb487bf2ff07fc1473" }, "ghcr.io/github/gh-aw-mcpg:v0.3.23": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.23", "digest": "sha256:0dd1bd91a41e24a3ccc31b1ec6cb61d36608997fabf91f2d643b64e3fc33180a", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.23", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.23@sha256:0dd1bd91a41e24a3ccc31b1ec6cb61d36608997fabf91f2d643b64e3fc33180a" }, "ghcr.io/github/gh-aw-mcpg:v0.3.24": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.24", "digest": "sha256:72cc921901afa1d67b6fee568f110c6b84436624c437fb2996d14c83e90a2b54", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.24", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.24@sha256:72cc921901afa1d67b6fee568f110c6b84436624c437fb2996d14c83e90a2b54" }, "ghcr.io/github/gh-aw-mcpg:v0.3.25": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.25", "digest": "sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.25", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa" }, "ghcr.io/github/gh-aw-mcpg:v0.3.26": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.26", "digest": "sha256:d3b03f54eee3a8176818c9a52087623e45b7f644a28814337fcc0838e2534490", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.26", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.26@sha256:d3b03f54eee3a8176818c9a52087623e45b7f644a28814337fcc0838e2534490" }, "ghcr.io/github/gh-aw-mcpg:v0.3.27": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.27", "digest": "sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.27", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7" }, "ghcr.io/github/gh-aw-mcpg:v0.3.29": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.29", "digest": "sha256:f24e968e6e5aeb819e9a98b9d273efe36b0fc1a0cb17e9ace263b5ab20de87cd", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.29", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.29@sha256:f24e968e6e5aeb819e9a98b9d273efe36b0fc1a0cb17e9ace263b5ab20de87cd" }, "ghcr.io/github/gh-aw-mcpg:v0.3.30": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.30", "digest": "sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.30", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be" }, "ghcr.io/github/gh-aw-mcpg:v0.3.31": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.31", "digest": "sha256:3147d7332939aeb2bc6a4220d96f6a0ca600b5cc0971200eca3d1e948fc8122a", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.31", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.31@sha256:3147d7332939aeb2bc6a4220d96f6a0ca600b5cc0971200eca3d1e948fc8122a" }, "ghcr.io/github/gh-aw-mcpg:v0.3.32": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.32", "digest": "sha256:63e46b56dfd70895a701b6fc6dd0189e11e2d875f327f1781e81b31848735477", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.32", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.32@sha256:63e46b56dfd70895a701b6fc6dd0189e11e2d875f327f1781e81b31848735477" }, "ghcr.io/github/gh-aw-mcpg:v0.3.33": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.33", "digest": "sha256:f0e1f1d562f01e737f3ef125cb4d7057ff87a68a97927f5ea9fd6d3f5091da06", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.33", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.33@sha256:f0e1f1d562f01e737f3ef125cb4d7057ff87a68a97927f5ea9fd6d3f5091da06" }, "ghcr.io/github/gh-aw-mcpg:v0.3.6": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.6", "digest": "sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.6", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c" }, "ghcr.io/github/gh-aw-mcpg:v0.3.8": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.8", "digest": "sha256:71e102af76e1ce10b455d2a2ef5717d9cc4c135a5db06fbadcb58e6c1a950cd4", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.8", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.8@sha256:71e102af76e1ce10b455d2a2ef5717d9cc4c135a5db06fbadcb58e6c1a950cd4" }, "ghcr.io/github/gh-aw-mcpg:v0.3.9": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.9", "digest": "sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.9", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388" }, "ghcr.io/github/gh-aw-mcpg:v0.4.0": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.4.0", "digest": "sha256:9dbdf42842c224a95016df1d2a85a2901e04204c242079343b302a307d2b8031", + "image": "ghcr.io/github/gh-aw-mcpg:v0.4.0", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.4.0@sha256:9dbdf42842c224a95016df1d2a85a2901e04204c242079343b302a307d2b8031" }, "ghcr.io/github/gh-aw-mcpg:v0.4.1": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.4.1", "digest": "sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32", + "image": "ghcr.io/github/gh-aw-mcpg:v0.4.1", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32" }, "ghcr.io/github/gh-aw-node": { - "image": "ghcr.io/github/gh-aw-node", "digest": "sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b", + "image": "ghcr.io/github/gh-aw-node", "pinned_image": "ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b" }, "ghcr.io/github/github-mcp-server:v0.32.0": { - "image": "ghcr.io/github/github-mcp-server:v0.32.0", "digest": "sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28", + "image": "ghcr.io/github/github-mcp-server:v0.32.0", "pinned_image": "ghcr.io/github/github-mcp-server:v0.32.0@sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28" }, "ghcr.io/github/github-mcp-server:v1.0.0": { - "image": "ghcr.io/github/github-mcp-server:v1.0.0", "digest": "sha256:d2550953f8050bc5a1c8f80d1678766f66f60bbfbcd953fdeaf661fe4269bd95", + "image": "ghcr.io/github/github-mcp-server:v1.0.0", "pinned_image": "ghcr.io/github/github-mcp-server:v1.0.0@sha256:d2550953f8050bc5a1c8f80d1678766f66f60bbfbcd953fdeaf661fe4269bd95" }, "ghcr.io/github/github-mcp-server:v1.0.3": { - "image": "ghcr.io/github/github-mcp-server:v1.0.3", "digest": "sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959", + "image": "ghcr.io/github/github-mcp-server:v1.0.3", "pinned_image": "ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959" }, "ghcr.io/github/github-mcp-server:v1.0.4": { - "image": "ghcr.io/github/github-mcp-server:v1.0.4", "digest": "sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4", + "image": "ghcr.io/github/github-mcp-server:v1.0.4", "pinned_image": "ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4" }, "ghcr.io/github/github-mcp-server:v1.1.0": { - "image": "ghcr.io/github/github-mcp-server:v1.1.0", "digest": "sha256:71b07d9abecb83b4a2595bcd8ccb35f9a0166361a12335f9e16da1ef07172029", + "image": "ghcr.io/github/github-mcp-server:v1.1.0", "pinned_image": "ghcr.io/github/github-mcp-server:v1.1.0@sha256:71b07d9abecb83b4a2595bcd8ccb35f9a0166361a12335f9e16da1ef07172029" }, "ghcr.io/github/github-mcp-server:v1.1.2": { - "image": "ghcr.io/github/github-mcp-server:v1.1.2", "digest": "sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c", + "image": "ghcr.io/github/github-mcp-server:v1.1.2", "pinned_image": "ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c" }, "ghcr.io/github/github-mcp-server:v1.3.0": { - "image": "ghcr.io/github/github-mcp-server:v1.3.0", "digest": "sha256:5c83359327a0bacc3d34db730bea6557d39d341cee0bf6c58c9a896e33150e80", + "image": "ghcr.io/github/github-mcp-server:v1.3.0", "pinned_image": "ghcr.io/github/github-mcp-server:v1.3.0@sha256:5c83359327a0bacc3d34db730bea6557d39d341cee0bf6c58c9a896e33150e80" }, "ghcr.io/github/github-mcp-server:v1.4.0": { - "image": "ghcr.io/github/github-mcp-server:v1.4.0", "digest": "sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036", + "image": "ghcr.io/github/github-mcp-server:v1.4.0", "pinned_image": "ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036" }, "ghcr.io/github/github-mcp-server:v1.5.0": { - "image": "ghcr.io/github/github-mcp-server:v1.5.0", "digest": "sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4", + "image": "ghcr.io/github/github-mcp-server:v1.5.0", "pinned_image": "ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4" }, "ghcr.io/github/serena-mcp-server:latest": { - "image": "ghcr.io/github/serena-mcp-server:latest", "digest": "sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5", + "image": "ghcr.io/github/serena-mcp-server:latest", "pinned_image": "ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5" }, "grafana/mcp-grafana": { - "image": "grafana/mcp-grafana", "digest": "sha256:60a4e3a417a69eeb864a72052c53b4aa4466ff3577d6ef9bacc671f4b77d7090", + "image": "grafana/mcp-grafana", "pinned_image": "grafana/mcp-grafana@sha256:60a4e3a417a69eeb864a72052c53b4aa4466ff3577d6ef9bacc671f4b77d7090" }, "mcp/arxiv-mcp-server": { - "image": "mcp/arxiv-mcp-server", "digest": "sha256:6dc6bba6dfed97f4ad6eb8d23a5c98ef5b7fa6184937d54b2d675801cd9dd29e", + "image": "mcp/arxiv-mcp-server", "pinned_image": "mcp/arxiv-mcp-server@sha256:6dc6bba6dfed97f4ad6eb8d23a5c98ef5b7fa6184937d54b2d675801cd9dd29e" }, "mcp/ast-grep:latest": { - "image": "mcp/ast-grep:latest", "digest": "sha256:5fc3f2e9dcf2c019e92662f608b8d89e12134ed6d91e6f5461de6efd506a1e72", + "image": "mcp/ast-grep:latest", "pinned_image": "mcp/ast-grep:latest@sha256:5fc3f2e9dcf2c019e92662f608b8d89e12134ed6d91e6f5461de6efd506a1e72" }, "mcp/context7": { - "image": "mcp/context7", "digest": "sha256:1174e6a29634a83b2be93ac1fefabf63265f498c02c72201fe3464e687dd8836", + "image": "mcp/context7", "pinned_image": "mcp/context7@sha256:1174e6a29634a83b2be93ac1fefabf63265f498c02c72201fe3464e687dd8836" }, "mcp/markitdown": { - "image": "mcp/markitdown", "digest": "sha256:1cef3bf502503310ed0884441874ccf6cdaac20136dc1179797fa048269dc4cb", + "image": "mcp/markitdown", "pinned_image": "mcp/markitdown@sha256:1cef3bf502503310ed0884441874ccf6cdaac20136dc1179797fa048269dc4cb" }, "mcp/memory": { - "image": "mcp/memory", "digest": "sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f", + "image": "mcp/memory", "pinned_image": "mcp/memory@sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f" }, "mcp/notion": { - "image": "mcp/notion", "digest": "sha256:4de8eb0de33402fcbd3740b4f4039918e4893155c7ea833c7a0c472001b88367", + "image": "mcp/notion", "pinned_image": "mcp/notion@sha256:4de8eb0de33402fcbd3740b4f4039918e4893155c7ea833c7a0c472001b88367" }, "mcr.microsoft.com/playwright/mcp": { - "image": "mcr.microsoft.com/playwright/mcp", "digest": "sha256:7b82f29c6ef83480a97f612d53ac3fd5f30a32df3fea1e06923d4204d3532bb2", + "image": "mcr.microsoft.com/playwright/mcp", "pinned_image": "mcr.microsoft.com/playwright/mcp@sha256:7b82f29c6ef83480a97f612d53ac3fd5f30a32df3fea1e06923d4204d3532bb2" }, "node:lts-alpine": { - "image": "node:lts-alpine", "digest": "sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14", + "image": "node:lts-alpine", "pinned_image": "node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14" }, "python:alpine": { - "image": "python:alpine", "digest": "sha256:6f873e340e6786787a632c919ecfb1d2301eb33ccfbe9f0d0add16cbc0892116", + "image": "python:alpine", "pinned_image": "python:alpine@sha256:6f873e340e6786787a632c919ecfb1d2301eb33ccfbe9f0d0add16cbc0892116" }, "semgrep/semgrep:latest": { - "image": "semgrep/semgrep:latest", "digest": "sha256:17d89ddd91a7729bbd5de09402f7f79a70204289e2a94635086e9db532a495f2", + "image": "semgrep/semgrep:latest", "pinned_image": "semgrep/semgrep:latest@sha256:17d89ddd91a7729bbd5de09402f7f79a70204289e2a94635086e9db532a495f2" } } diff --git a/.github/workflows/approach-validator.lock.yml b/.github/workflows/approach-validator.lock.yml index 8c08928a3c0..8a96b3e308b 100644 --- a/.github/workflows/approach-validator.lock.yml +++ b/.github/workflows/approach-validator.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"af1b9dc816a350c2f49e5efa740597641814f4b26320303a976c8229871912c6","body_hash":"d0c71d6e5a3ef83b1d26ff612fc181ffc828c605f3c8b0ba44903a9f118b7b44","strict":true,"agent_id":"claude","engine_versions":{"claude":"2.1.201"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"5d6380374e707498e4e84e1fec38ab1e090173c9af07a902586046395ae2ffaf","body_hash":"d0c71d6e5a3ef83b1d26ff612fc181ffc828c605f3c8b0ba44903a9f118b7b44","strict":true,"agent_id":"claude","engine_versions":{"claude":"2.1.201"}} # gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29","digest":"sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29@sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29","digest":"sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29@sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29","digest":"sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29@sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29","digest":"sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29@sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -1213,6 +1213,7 @@ jobs: needs: - activation - agent + - content_redaction - detection - safe_outputs if: > @@ -1499,6 +1500,126 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + content_redaction: + needs: + - activation + - agent + - detection + if: > + always() && needs.agent.result != 'skipped' && (needs.detection.result == 'success' || needs.detection.result == 'skipped') + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + redaction_conclusion: ${{ steps.redaction_conclusion.outputs.conclusion }} + redaction_success: ${{ steps.redaction_conclusion.outputs.success }} + steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false + - name: Setup Scripts + id: setup + uses: ./actions/setup + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Approach Validator" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/approach-validator.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "2.1.201" + GH_AW_INFO_AWF_VERSION: "v0.27.29" + GH_AW_INFO_ENGINE_ID: "claude" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Load redaction policies + id: load_policies + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + mkdir -p /tmp/gh-aw/content-redaction + POLICY_FILE=/tmp/gh-aw/content-redaction/policy.md + : > "$POLICY_FILE" + # Policy source 1: Do not disclose security vulnerabilities, internal credentia... + printf '%s\n' 'Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output.' >> "$POLICY_FILE" + echo '' >> "$POLICY_FILE" + # Verify policy file is not empty + if [ ! -s "$POLICY_FILE" ]; then + echo '::error::Content redaction: all policy sources failed or were empty' + exit 1 + fi + echo "Policy loaded ($(wc -c < "$POLICY_FILE") bytes)" + - name: Run content redaction + id: run_redaction + if: always() && steps.load_policies.outcome == 'success' + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + POLICY_FILE: /tmp/gh-aw/content-redaction/policy.md + REDACTION_MODEL: gpt-4o-mini + ON_FAILURE: block + SCOPE: '[]' + run: | + set -e + # Resolve node executable + GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}" + if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then + GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)" + fi + if [ -z "$GH_AW_NODE_EXEC" ]; then + echo "::error::node runtime missing on this runner — check runtimes.node in workflow YAML" + exit 127 + fi + # Configure AWF for content redaction + AWF_CONFIG_FILE="${RUNNER_TEMP}/gh-aw/content-redaction-awf-config.json" + printf '%s\n' '{"apiProxy":{"enabled":true}}' > "${AWF_CONFIG_FILE}" + # Execute content redaction script via AWF + awf --config "${AWF_CONFIG_FILE}" \ + --container-workdir "${GITHUB_WORKSPACE}" \ + --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:rw" \ + --mount "/tmp/gh-aw:/tmp/gh-aw:rw" \ + --env-all \ + --log-level info \ + --skip-pull \ + -- bash -c "\"\$GH_AW_NODE_EXEC\" \"\${RUNNER_TEMP}/gh-aw/actions/content_redaction.cjs\"" + - name: Set redaction conclusion + id: redaction_conclusion + if: always() + env: + HAS_BLOCKED: ${{ steps.run_redaction.outputs.has_blocked_items }} + SKIPPED: ${{ steps.run_redaction.outputs.skipped }} + run: | + if [[ "$SKIPPED" == "true" ]]; then + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + elif [[ "$HAS_BLOCKED" == "true" && "block" == "block" ]]; then + echo "success=false" >> "$GITHUB_OUTPUT" + echo "conclusion=blocked" >> "$GITHUB_OUTPUT" + echo "::error::Content redaction blocked items that could not be made policy-compliant" + exit 1 + else + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=passed" >> "$GITHUB_OUTPUT" + fi + detection: needs: - activation @@ -1788,8 +1909,10 @@ jobs: needs: - activation - agent + - content_redaction - detection - if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' + if: > + (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' && needs.content_redaction.outputs.redaction_success == 'true' runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/approach-validator.md b/.github/workflows/approach-validator.md index 5d4a133e66f..4be5a75a8f7 100644 --- a/.github/workflows/approach-validator.md +++ b/.github/workflows/approach-validator.md @@ -44,6 +44,7 @@ safe-outputs: max: 1 allowed: ["awaiting-approach-approval", "approach-approved", "approach-rejected"] noop: + content-redaction: "Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output." messages: footer: "> 🔬 *Approach validated by [{workflow_name}]({run_url})*{ai_credits_suffix}{history_link}" run-started: "🔬 [{workflow_name}]({run_url}) is analyzing the proposed approach on this {event_type}..." diff --git a/.github/workflows/archie.lock.yml b/.github/workflows/archie.lock.yml index ea90393a765..a1381620b26 100644 --- a/.github/workflows/archie.lock.yml +++ b/.github/workflows/archie.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"301759a3f678d1e0d19702279b9930e36efe657bf68bb2580275436ebaede21c","body_hash":"60a18e00a12975cd2b35b2515f30f30d72c747701f8b531fdd8859e7cd48493a","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.68"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"8bdc8fabd8fdbdda21fa27bce683f2ccd268ec8448a5ecd04bbd4f2d01537f72","body_hash":"60a18e00a12975cd2b35b2515f30f30d72c747701f8b531fdd8859e7cd48493a","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.68"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29","digest":"sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29@sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29","digest":"sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29@sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29","digest":"sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29@sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29","digest":"sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29@sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"},{"image":"ghcr.io/github/serena-mcp-server:latest","digest":"sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5","pinned_image":"ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -1105,6 +1105,7 @@ jobs: needs: - activation - agent + - content_redaction - detection - safe_outputs if: > @@ -1390,6 +1391,127 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + content_redaction: + needs: + - activation + - agent + - detection + if: > + always() && needs.agent.result != 'skipped' && (needs.detection.result == 'success' || needs.detection.result == 'skipped') + runs-on: ubuntu-latest + permissions: + contents: read + copilot-requests: write + outputs: + redaction_conclusion: ${{ steps.redaction_conclusion.outputs.conclusion }} + redaction_success: ${{ steps.redaction_conclusion.outputs.success }} + steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false + - name: Setup Scripts + id: setup + uses: ./actions/setup + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Archie" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/archie.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.68" + GH_AW_INFO_AWF_VERSION: "v0.27.29" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Load redaction policies + id: load_policies + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + mkdir -p /tmp/gh-aw/content-redaction + POLICY_FILE=/tmp/gh-aw/content-redaction/policy.md + : > "$POLICY_FILE" + # Policy source 1: Do not disclose security vulnerabilities, internal credentia... + printf '%s\n' 'Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output.' >> "$POLICY_FILE" + echo '' >> "$POLICY_FILE" + # Verify policy file is not empty + if [ ! -s "$POLICY_FILE" ]; then + echo '::error::Content redaction: all policy sources failed or were empty' + exit 1 + fi + echo "Policy loaded ($(wc -c < "$POLICY_FILE") bytes)" + - name: Run content redaction + id: run_redaction + if: always() && steps.load_policies.outcome == 'success' + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + POLICY_FILE: /tmp/gh-aw/content-redaction/policy.md + REDACTION_MODEL: gpt-4o-mini + ON_FAILURE: block + SCOPE: '[]' + run: | + set -e + # Resolve node executable + GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}" + if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then + GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)" + fi + if [ -z "$GH_AW_NODE_EXEC" ]; then + echo "::error::node runtime missing on this runner — check runtimes.node in workflow YAML" + exit 127 + fi + # Configure AWF for content redaction + AWF_CONFIG_FILE="${RUNNER_TEMP}/gh-aw/content-redaction-awf-config.json" + printf '%s\n' '{"apiProxy":{"enabled":true}}' > "${AWF_CONFIG_FILE}" + # Execute content redaction script via AWF + awf --config "${AWF_CONFIG_FILE}" \ + --container-workdir "${GITHUB_WORKSPACE}" \ + --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:rw" \ + --mount "/tmp/gh-aw:/tmp/gh-aw:rw" \ + --env-all \ + --log-level info \ + --skip-pull \ + -- bash -c "\"\$GH_AW_NODE_EXEC\" \"\${RUNNER_TEMP}/gh-aw/actions/content_redaction.cjs\"" + - name: Set redaction conclusion + id: redaction_conclusion + if: always() + env: + HAS_BLOCKED: ${{ steps.run_redaction.outputs.has_blocked_items }} + SKIPPED: ${{ steps.run_redaction.outputs.skipped }} + run: | + if [[ "$SKIPPED" == "true" ]]; then + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + elif [[ "$HAS_BLOCKED" == "true" && "block" == "block" ]]; then + echo "success=false" >> "$GITHUB_OUTPUT" + echo "conclusion=blocked" >> "$GITHUB_OUTPUT" + echo "::error::Content redaction blocked items that could not be made policy-compliant" + exit 1 + else + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=passed" >> "$GITHUB_OUTPUT" + fi + detection: needs: - activation @@ -1674,8 +1796,10 @@ jobs: needs: - activation - agent + - content_redaction - detection - if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' + if: > + (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' && needs.content_redaction.outputs.redaction_success == 'true' runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/archie.md b/.github/workflows/archie.md index 0347ce1b34c..feb22d6570b 100644 --- a/.github/workflows/archie.md +++ b/.github/workflows/archie.md @@ -35,6 +35,7 @@ tools: safe-outputs: add-comment: max: 1 + content-redaction: "Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output." messages: footer: "> 📊 *Diagram rendered by [{workflow_name}]({run_url})*{ai_credits_suffix}{history_link}" footer-workflow-recompile: "> 🔧 *Workflow sync report by [{workflow_name}]({run_url}) for {repository}*" diff --git a/.github/workflows/auto-triage-issues.lock.yml b/.github/workflows/auto-triage-issues.lock.yml index 5a187df5120..16801f43d80 100644 --- a/.github/workflows/auto-triage-issues.lock.yml +++ b/.github/workflows/auto-triage-issues.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"bb35fc99c4c64a8eda549409eef4d408a1fa683bf3223c32c61bb50a54c508ac","body_hash":"dce24b720a732abc69ab73ff711635f682259aac4684b2a8533b85ffaeb3edc8","strict":true,"agent_id":"pi","agent_model":"copilot/gpt-5.4","engine_versions":{"pi":"0.80.3"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"136dead4ad000ef1e05999f9210814e146d26603b73fba26a8872402a46d8024","body_hash":"dce24b720a732abc69ab73ff711635f682259aac4684b2a8533b85ffaeb3edc8","strict":true,"agent_id":"pi","agent_model":"copilot/gpt-5.4","engine_versions":{"pi":"0.80.3"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29","digest":"sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29@sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29","digest":"sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29@sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29","digest":"sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29@sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29","digest":"sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29@sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -1061,6 +1061,7 @@ jobs: needs: - activation - agent + - content_redaction - detection - safe_outputs if: > @@ -1321,6 +1322,127 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + content_redaction: + needs: + - activation + - agent + - detection + if: > + always() && needs.agent.result != 'skipped' && (needs.detection.result == 'success' || needs.detection.result == 'skipped') + runs-on: ubuntu-latest + permissions: + contents: read + copilot-requests: write + outputs: + redaction_conclusion: ${{ steps.redaction_conclusion.outputs.conclusion }} + redaction_success: ${{ steps.redaction_conclusion.outputs.success }} + steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false + - name: Setup Scripts + id: setup + uses: ./actions/setup + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Auto-Triage Issues" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/auto-triage-issues.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "0.80.3" + GH_AW_INFO_AWF_VERSION: "v0.27.29" + GH_AW_INFO_ENGINE_ID: "pi" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Load redaction policies + id: load_policies + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + mkdir -p /tmp/gh-aw/content-redaction + POLICY_FILE=/tmp/gh-aw/content-redaction/policy.md + : > "$POLICY_FILE" + # Policy source 1: Do not disclose security vulnerabilities, internal credentia... + printf '%s\n' 'Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output.' >> "$POLICY_FILE" + echo '' >> "$POLICY_FILE" + # Verify policy file is not empty + if [ ! -s "$POLICY_FILE" ]; then + echo '::error::Content redaction: all policy sources failed or were empty' + exit 1 + fi + echo "Policy loaded ($(wc -c < "$POLICY_FILE") bytes)" + - name: Run content redaction + id: run_redaction + if: always() && steps.load_policies.outcome == 'success' + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + POLICY_FILE: /tmp/gh-aw/content-redaction/policy.md + REDACTION_MODEL: gpt-4o-mini + ON_FAILURE: block + SCOPE: '[]' + run: | + set -e + # Resolve node executable + GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}" + if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then + GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)" + fi + if [ -z "$GH_AW_NODE_EXEC" ]; then + echo "::error::node runtime missing on this runner — check runtimes.node in workflow YAML" + exit 127 + fi + # Configure AWF for content redaction + AWF_CONFIG_FILE="${RUNNER_TEMP}/gh-aw/content-redaction-awf-config.json" + printf '%s\n' '{"apiProxy":{"enabled":true}}' > "${AWF_CONFIG_FILE}" + # Execute content redaction script via AWF + awf --config "${AWF_CONFIG_FILE}" \ + --container-workdir "${GITHUB_WORKSPACE}" \ + --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:rw" \ + --mount "/tmp/gh-aw:/tmp/gh-aw:rw" \ + --env-all \ + --log-level info \ + --skip-pull \ + -- bash -c "\"\$GH_AW_NODE_EXEC\" \"\${RUNNER_TEMP}/gh-aw/actions/content_redaction.cjs\"" + - name: Set redaction conclusion + id: redaction_conclusion + if: always() + env: + HAS_BLOCKED: ${{ steps.run_redaction.outputs.has_blocked_items }} + SKIPPED: ${{ steps.run_redaction.outputs.skipped }} + run: | + if [[ "$SKIPPED" == "true" ]]; then + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + elif [[ "$HAS_BLOCKED" == "true" && "block" == "block" ]]; then + echo "success=false" >> "$GITHUB_OUTPUT" + echo "conclusion=blocked" >> "$GITHUB_OUTPUT" + echo "::error::Content redaction blocked items that could not be made policy-compliant" + exit 1 + else + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=passed" >> "$GITHUB_OUTPUT" + fi + detection: needs: - activation @@ -1610,8 +1732,10 @@ jobs: needs: - activation - agent + - content_redaction - detection - if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' + if: > + (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' && needs.content_redaction.outputs.redaction_success == 'true' runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/auto-triage-issues.md b/.github/workflows/auto-triage-issues.md index ca733576b30..88a1a76f0a3 100644 --- a/.github/workflows/auto-triage-issues.md +++ b/.github/workflows/auto-triage-issues.md @@ -57,6 +57,7 @@ safe-outputs: close-older-discussions: true max: 1 noop: + content-redaction: "Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output." timeout-minutes: 15 features: gh-aw-detection: true diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index bf8a29adc74..a5fbf8ac692 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"54cd251a064f7dedb4e324bf8c2ba43bd9708fef1fbbea14f2994b641abfedc7","body_hash":"02b5abc99def3b9438f3d8ede508c4daad9d1acc708df529028032f541495001","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.68","copilot-sdk":"1.0.5"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a7e84b8e622a47d976cf04abf57057e20701fd6080c9428c6d36ac8828b713f3","body_hash":"02b5abc99def3b9438f3d8ede508c4daad9d1acc708df529028032f541495001","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.68","copilot-sdk":"1.0.5"}} # gh-aw-manifest: {"version":1,"secrets":["BRAVE_API_KEY","COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"docker.io/mcp/brave-search","digest":"sha256:ca96b8acb27d8cf601a8faef86a084602cffa41d8cb18caa1e29ba4d16989d22","pinned_image":"docker.io/mcp/brave-search@sha256:ca96b8acb27d8cf601a8faef86a084602cffa41d8cb18caa1e29ba4d16989d22"},{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29","digest":"sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29@sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29","digest":"sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29@sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29","digest":"sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29@sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -1094,6 +1094,7 @@ jobs: needs: - activation - agent + - content_redaction - detection - safe_outputs if: > @@ -1379,6 +1380,127 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + content_redaction: + needs: + - activation + - agent + - detection + if: > + always() && needs.agent.result != 'skipped' && (needs.detection.result == 'success' || needs.detection.result == 'skipped') + runs-on: ubuntu-latest + permissions: + contents: read + copilot-requests: write + outputs: + redaction_conclusion: ${{ steps.redaction_conclusion.outputs.conclusion }} + redaction_success: ${{ steps.redaction_conclusion.outputs.success }} + steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false + - name: Setup Scripts + id: setup + uses: ./actions/setup + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Brave Web Search Agent" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/brave.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.68" + GH_AW_INFO_AWF_VERSION: "v0.27.29" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Load redaction policies + id: load_policies + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + mkdir -p /tmp/gh-aw/content-redaction + POLICY_FILE=/tmp/gh-aw/content-redaction/policy.md + : > "$POLICY_FILE" + # Policy source 1: Do not disclose security vulnerabilities, internal credentia... + printf '%s\n' 'Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output.' >> "$POLICY_FILE" + echo '' >> "$POLICY_FILE" + # Verify policy file is not empty + if [ ! -s "$POLICY_FILE" ]; then + echo '::error::Content redaction: all policy sources failed or were empty' + exit 1 + fi + echo "Policy loaded ($(wc -c < "$POLICY_FILE") bytes)" + - name: Run content redaction + id: run_redaction + if: always() && steps.load_policies.outcome == 'success' + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + POLICY_FILE: /tmp/gh-aw/content-redaction/policy.md + REDACTION_MODEL: gpt-4o-mini + ON_FAILURE: block + SCOPE: '[]' + run: | + set -e + # Resolve node executable + GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}" + if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then + GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)" + fi + if [ -z "$GH_AW_NODE_EXEC" ]; then + echo "::error::node runtime missing on this runner — check runtimes.node in workflow YAML" + exit 127 + fi + # Configure AWF for content redaction + AWF_CONFIG_FILE="${RUNNER_TEMP}/gh-aw/content-redaction-awf-config.json" + printf '%s\n' '{"apiProxy":{"enabled":true}}' > "${AWF_CONFIG_FILE}" + # Execute content redaction script via AWF + awf --config "${AWF_CONFIG_FILE}" \ + --container-workdir "${GITHUB_WORKSPACE}" \ + --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:rw" \ + --mount "/tmp/gh-aw:/tmp/gh-aw:rw" \ + --env-all \ + --log-level info \ + --skip-pull \ + -- bash -c "\"\$GH_AW_NODE_EXEC\" \"\${RUNNER_TEMP}/gh-aw/actions/content_redaction.cjs\"" + - name: Set redaction conclusion + id: redaction_conclusion + if: always() + env: + HAS_BLOCKED: ${{ steps.run_redaction.outputs.has_blocked_items }} + SKIPPED: ${{ steps.run_redaction.outputs.skipped }} + run: | + if [[ "$SKIPPED" == "true" ]]; then + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + elif [[ "$HAS_BLOCKED" == "true" && "block" == "block" ]]; then + echo "success=false" >> "$GITHUB_OUTPUT" + echo "conclusion=blocked" >> "$GITHUB_OUTPUT" + echo "::error::Content redaction blocked items that could not be made policy-compliant" + exit 1 + else + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=passed" >> "$GITHUB_OUTPUT" + fi + detection: needs: - activation @@ -1663,8 +1785,10 @@ jobs: needs: - activation - agent + - content_redaction - detection - if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' + if: > + (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' && needs.content_redaction.outputs.redaction_success == 'true' runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/brave.md b/.github/workflows/brave.md index cca09c9d78f..da28492e5b2 100644 --- a/.github/workflows/brave.md +++ b/.github/workflows/brave.md @@ -23,6 +23,7 @@ imports: safe-outputs: add-comment: max: 1 + content-redaction: "Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output." messages: footer: "> 🦁 *Search results brought to you by [{workflow_name}]({run_url})*{ai_credits_suffix}{history_link}" footer-workflow-recompile: "> 🔄 *Maintenance report by [{workflow_name}]({run_url}) for {repository}*" diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 90b02fbd4c1..a87794fea70 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"f9439c24f8a51ee623d46b8a0da03b87e830a8175f6e6d3ee106c8e46e21ef3a","body_hash":"c5646d7ad137f2fbba9ba94628a5dd8c50476921c14a8ce75073e54590a6876e","strict":true,"agent_id":"claude","engine_versions":{"claude":"2.1.201"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"07e11b666e82de465abca630f9014ac26fe046581adcab77feb4bf54048d91e7","body_hash":"c5646d7ad137f2fbba9ba94628a5dd8c50476921c14a8ce75073e54590a6876e","strict":true,"agent_id":"claude","engine_versions":{"claude":"2.1.201"}} # gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29","digest":"sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29@sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29","digest":"sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29@sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29","digest":"sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29@sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29","digest":"sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29@sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -1315,6 +1315,7 @@ jobs: needs: - activation - agent + - content_redaction - detection - safe_outputs - update_cache_memory @@ -1611,6 +1612,127 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + content_redaction: + needs: + - activation + - agent + - detection + if: > + always() && needs.agent.result != 'skipped' && (needs.detection.result == 'success' || needs.detection.result == 'skipped') + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + redaction_conclusion: ${{ steps.redaction_conclusion.outputs.conclusion }} + redaction_success: ${{ steps.redaction_conclusion.outputs.success }} + steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false + - name: Setup Scripts + id: setup + uses: ./actions/setup + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "CI Failure Doctor" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/ci-doctor.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "2.1.201" + GH_AW_INFO_AWF_VERSION: "v0.27.29" + GH_AW_INFO_BODY_MODIFIED: "false" + GH_AW_INFO_ENGINE_ID: "claude" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Load redaction policies + id: load_policies + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + mkdir -p /tmp/gh-aw/content-redaction + POLICY_FILE=/tmp/gh-aw/content-redaction/policy.md + : > "$POLICY_FILE" + # Policy source 1: Do not disclose security vulnerabilities, internal credentia... + printf '%s\n' 'Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output.' >> "$POLICY_FILE" + echo '' >> "$POLICY_FILE" + # Verify policy file is not empty + if [ ! -s "$POLICY_FILE" ]; then + echo '::error::Content redaction: all policy sources failed or were empty' + exit 1 + fi + echo "Policy loaded ($(wc -c < "$POLICY_FILE") bytes)" + - name: Run content redaction + id: run_redaction + if: always() && steps.load_policies.outcome == 'success' + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + POLICY_FILE: /tmp/gh-aw/content-redaction/policy.md + REDACTION_MODEL: gpt-4o-mini + ON_FAILURE: block + SCOPE: '[]' + run: | + set -e + # Resolve node executable + GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}" + if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then + GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)" + fi + if [ -z "$GH_AW_NODE_EXEC" ]; then + echo "::error::node runtime missing on this runner — check runtimes.node in workflow YAML" + exit 127 + fi + # Configure AWF for content redaction + AWF_CONFIG_FILE="${RUNNER_TEMP}/gh-aw/content-redaction-awf-config.json" + printf '%s\n' '{"apiProxy":{"enabled":true}}' > "${AWF_CONFIG_FILE}" + # Execute content redaction script via AWF + awf --config "${AWF_CONFIG_FILE}" \ + --container-workdir "${GITHUB_WORKSPACE}" \ + --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:rw" \ + --mount "/tmp/gh-aw:/tmp/gh-aw:rw" \ + --env-all \ + --log-level info \ + --skip-pull \ + -- bash -c "\"\$GH_AW_NODE_EXEC\" \"\${RUNNER_TEMP}/gh-aw/actions/content_redaction.cjs\"" + - name: Set redaction conclusion + id: redaction_conclusion + if: always() + env: + HAS_BLOCKED: ${{ steps.run_redaction.outputs.has_blocked_items }} + SKIPPED: ${{ steps.run_redaction.outputs.skipped }} + run: | + if [[ "$SKIPPED" == "true" ]]; then + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + elif [[ "$HAS_BLOCKED" == "true" && "block" == "block" ]]; then + echo "success=false" >> "$GITHUB_OUTPUT" + echo "conclusion=blocked" >> "$GITHUB_OUTPUT" + echo "::error::Content redaction blocked items that could not be made policy-compliant" + exit 1 + else + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=passed" >> "$GITHUB_OUTPUT" + fi + detection: needs: - activation @@ -1889,8 +2011,10 @@ jobs: needs: - activation - agent + - content_redaction - detection - if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' + if: > + (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' && needs.content_redaction.outputs.redaction_success == 'true' runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/ci-doctor.md b/.github/workflows/ci-doctor.md index 90c4548ade3..6ebb0ddc534 100644 --- a/.github/workflows/ci-doctor.md +++ b/.github/workflows/ci-doctor.md @@ -30,6 +30,7 @@ safe-outputs: hide-older-comments: true update-issue: noop: + content-redaction: "Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output." messages: footer: "> 🩺 *Diagnosis provided by [{workflow_name}]({run_url})*{ai_credits_suffix}{history_link}" run-started: "🏥 CI Doctor reporting for duty! [{workflow_name}]({run_url}) is examining the patient on this {event_type}..." diff --git a/.github/workflows/contribution-check.lock.yml b/.github/workflows/contribution-check.lock.yml index 93fd9b2b274..bc9f5990124 100644 --- a/.github/workflows/contribution-check.lock.yml +++ b/.github/workflows/contribution-check.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"5cd086c3949fec41c21620caeaaa7ea56f51823e06eed0ab72eb2347b2cbad15","body_hash":"352f47c4d2a6f0cd4ae8eaaf76aa067f6380b3fd3c6d3e0cfe237515f0d2de73","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.68"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"44daeb3b7e2bc80ac79685d92e659e1d9621a2acd297b99e2fbae09aac687c91","body_hash":"352f47c4d2a6f0cd4ae8eaaf76aa067f6380b3fd3c6d3e0cfe237515f0d2de73","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.68"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29","digest":"sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29@sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29","digest":"sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29@sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29","digest":"sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29@sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29","digest":"sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29@sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -1172,6 +1172,7 @@ jobs: needs: - activation - agent + - content_redaction - detection - safe_outputs if: > @@ -1436,6 +1437,126 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + content_redaction: + needs: + - activation + - agent + - detection + if: > + always() && needs.agent.result != 'skipped' && (needs.detection.result == 'success' || needs.detection.result == 'skipped') + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + redaction_conclusion: ${{ steps.redaction_conclusion.outputs.conclusion }} + redaction_success: ${{ steps.redaction_conclusion.outputs.success }} + steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false + - name: Setup Scripts + id: setup + uses: ./actions/setup + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Contribution Check" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/contribution-check.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.68" + GH_AW_INFO_AWF_VERSION: "v0.27.29" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Load redaction policies + id: load_policies + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + mkdir -p /tmp/gh-aw/content-redaction + POLICY_FILE=/tmp/gh-aw/content-redaction/policy.md + : > "$POLICY_FILE" + # Policy source 1: Do not disclose security vulnerabilities, internal credentia... + printf '%s\n' 'Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output.' >> "$POLICY_FILE" + echo '' >> "$POLICY_FILE" + # Verify policy file is not empty + if [ ! -s "$POLICY_FILE" ]; then + echo '::error::Content redaction: all policy sources failed or were empty' + exit 1 + fi + echo "Policy loaded ($(wc -c < "$POLICY_FILE") bytes)" + - name: Run content redaction + id: run_redaction + if: always() && steps.load_policies.outcome == 'success' + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + POLICY_FILE: /tmp/gh-aw/content-redaction/policy.md + REDACTION_MODEL: gpt-4o-mini + ON_FAILURE: block + SCOPE: '[]' + run: | + set -e + # Resolve node executable + GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}" + if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then + GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)" + fi + if [ -z "$GH_AW_NODE_EXEC" ]; then + echo "::error::node runtime missing on this runner — check runtimes.node in workflow YAML" + exit 127 + fi + # Configure AWF for content redaction + AWF_CONFIG_FILE="${RUNNER_TEMP}/gh-aw/content-redaction-awf-config.json" + printf '%s\n' '{"apiProxy":{"enabled":true}}' > "${AWF_CONFIG_FILE}" + # Execute content redaction script via AWF + awf --config "${AWF_CONFIG_FILE}" \ + --container-workdir "${GITHUB_WORKSPACE}" \ + --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:rw" \ + --mount "/tmp/gh-aw:/tmp/gh-aw:rw" \ + --env-all \ + --log-level info \ + --skip-pull \ + -- bash -c "\"\$GH_AW_NODE_EXEC\" \"\${RUNNER_TEMP}/gh-aw/actions/content_redaction.cjs\"" + - name: Set redaction conclusion + id: redaction_conclusion + if: always() + env: + HAS_BLOCKED: ${{ steps.run_redaction.outputs.has_blocked_items }} + SKIPPED: ${{ steps.run_redaction.outputs.skipped }} + run: | + if [[ "$SKIPPED" == "true" ]]; then + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + elif [[ "$HAS_BLOCKED" == "true" && "block" == "block" ]]; then + echo "success=false" >> "$GITHUB_OUTPUT" + echo "conclusion=blocked" >> "$GITHUB_OUTPUT" + echo "::error::Content redaction blocked items that could not be made policy-compliant" + exit 1 + else + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=passed" >> "$GITHUB_OUTPUT" + fi + detection: needs: - activation @@ -1661,8 +1782,10 @@ jobs: needs: - activation - agent + - content_redaction - detection - if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' + if: > + (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' && needs.content_redaction.outputs.redaction_success == 'true' runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/contribution-check.md b/.github/workflows/contribution-check.md index f17b5f73dcc..d9fb92785d6 100644 --- a/.github/workflows/contribution-check.md +++ b/.github/workflows/contribution-check.md @@ -51,6 +51,7 @@ safe-outputs: target: "*" target-repo: ${{ vars.TARGET_REPOSITORY }} hide-older-comments: true + content-redaction: "Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output." steps: - name: Fetch and filter PRs env: diff --git a/.github/workflows/craft.lock.yml b/.github/workflows/craft.lock.yml index 9bd5d0dc79a..817a082af70 100644 --- a/.github/workflows/craft.lock.yml +++ b/.github/workflows/craft.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"96730c0fc8ea2aeb896c51ea4a6089402bfc1e8ede579c1c87c95e5bc2bd5bf9","body_hash":"c5ca4bdc488ffef1a0584eb571f48281106caa7b096cf3d3fa908e03d122de19","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.68","copilot-sdk":"1.0.5"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"333ca6dddf8085eee7f788fff4bbd6ba32ef5b4fcd99bd5d8359df28ad958817","body_hash":"c5ca4bdc488ffef1a0584eb571f48281106caa7b096cf3d3fa908e03d122de19","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.68","copilot-sdk":"1.0.5"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29","digest":"sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29@sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29","digest":"sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29@sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29","digest":"sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29@sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29","digest":"sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29@sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -1103,6 +1103,7 @@ jobs: needs: - activation - agent + - content_redaction - detection - safe_outputs if: > @@ -1390,6 +1391,127 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + content_redaction: + needs: + - activation + - agent + - detection + if: > + always() && needs.agent.result != 'skipped' && (needs.detection.result == 'success' || needs.detection.result == 'skipped') + runs-on: ubuntu-latest + permissions: + contents: read + copilot-requests: write + outputs: + redaction_conclusion: ${{ steps.redaction_conclusion.outputs.conclusion }} + redaction_success: ${{ steps.redaction_conclusion.outputs.success }} + steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false + - name: Setup Scripts + id: setup + uses: ./actions/setup + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Workflow Craft Agent" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/craft.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.68" + GH_AW_INFO_AWF_VERSION: "v0.27.29" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Load redaction policies + id: load_policies + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + mkdir -p /tmp/gh-aw/content-redaction + POLICY_FILE=/tmp/gh-aw/content-redaction/policy.md + : > "$POLICY_FILE" + # Policy source 1: Do not disclose security vulnerabilities, internal credentia... + printf '%s\n' 'Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output.' >> "$POLICY_FILE" + echo '' >> "$POLICY_FILE" + # Verify policy file is not empty + if [ ! -s "$POLICY_FILE" ]; then + echo '::error::Content redaction: all policy sources failed or were empty' + exit 1 + fi + echo "Policy loaded ($(wc -c < "$POLICY_FILE") bytes)" + - name: Run content redaction + id: run_redaction + if: always() && steps.load_policies.outcome == 'success' + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + POLICY_FILE: /tmp/gh-aw/content-redaction/policy.md + REDACTION_MODEL: gpt-4o-mini + ON_FAILURE: block + SCOPE: '[]' + run: | + set -e + # Resolve node executable + GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}" + if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then + GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)" + fi + if [ -z "$GH_AW_NODE_EXEC" ]; then + echo "::error::node runtime missing on this runner — check runtimes.node in workflow YAML" + exit 127 + fi + # Configure AWF for content redaction + AWF_CONFIG_FILE="${RUNNER_TEMP}/gh-aw/content-redaction-awf-config.json" + printf '%s\n' '{"apiProxy":{"enabled":true}}' > "${AWF_CONFIG_FILE}" + # Execute content redaction script via AWF + awf --config "${AWF_CONFIG_FILE}" \ + --container-workdir "${GITHUB_WORKSPACE}" \ + --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:rw" \ + --mount "/tmp/gh-aw:/tmp/gh-aw:rw" \ + --env-all \ + --log-level info \ + --skip-pull \ + -- bash -c "\"\$GH_AW_NODE_EXEC\" \"\${RUNNER_TEMP}/gh-aw/actions/content_redaction.cjs\"" + - name: Set redaction conclusion + id: redaction_conclusion + if: always() + env: + HAS_BLOCKED: ${{ steps.run_redaction.outputs.has_blocked_items }} + SKIPPED: ${{ steps.run_redaction.outputs.skipped }} + run: | + if [[ "$SKIPPED" == "true" ]]; then + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + elif [[ "$HAS_BLOCKED" == "true" && "block" == "block" ]]; then + echo "success=false" >> "$GITHUB_OUTPUT" + echo "conclusion=blocked" >> "$GITHUB_OUTPUT" + echo "::error::Content redaction blocked items that could not be made policy-compliant" + exit 1 + else + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=passed" >> "$GITHUB_OUTPUT" + fi + detection: needs: - activation @@ -1674,8 +1796,10 @@ jobs: needs: - activation - agent + - content_redaction - detection - if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' + if: > + (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' && needs.content_redaction.outputs.redaction_success == 'true' runs-on: ubuntu-slim permissions: contents: write diff --git a/.github/workflows/craft.md b/.github/workflows/craft.md index 40b2bfb91ff..eaaa33efb24 100644 --- a/.github/workflows/craft.md +++ b/.github/workflows/craft.md @@ -41,6 +41,7 @@ safe-outputs: add-comment: max: 1 push-to-pull-request-branch: + content-redaction: "Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output." messages: footer: "> ⚒️ *Crafted with care by [{workflow_name}]({run_url})*{ai_credits_suffix}{history_link}" run-started: "🛠️ Master Crafter at work! [{workflow_name}]({run_url}) is forging a new workflow on this {event_type}..." diff --git a/.github/workflows/dev-hawk.lock.yml b/.github/workflows/dev-hawk.lock.yml index ef8b8eee918..da18f4da330 100644 --- a/.github/workflows/dev-hawk.lock.yml +++ b/.github/workflows/dev-hawk.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"c274d66c18e4f1649762dbdf7cd2aba2a79a75f66aec8dfcfdf78ae5878dfd1d","body_hash":"2852b4fd3b4663d4e378577fd94a85f62f76a339c6598f323d52a5036168cd3d","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.68","copilot-sdk":"1.0.5"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"080e4bc5e05c5a601d851aa989f9a60fe00a5fd179cfc276d6b613b0c0e15863","body_hash":"2852b4fd3b4663d4e378577fd94a85f62f76a339c6598f323d52a5036168cd3d","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.68","copilot-sdk":"1.0.5"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"924ae3a1cded613372ab5595356fb5720e22ba16","version":"v6.5.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"docker/build-push-action","sha":"f9f3042f7e2789586610d6e8b85c8f03e5195baf","version":"v7.2.0"},{"repo":"docker/setup-buildx-action","sha":"d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5","version":"v4.1.0"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29","digest":"sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29@sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29","digest":"sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29@sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29","digest":"sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29@sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29","digest":"sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29@sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -1151,6 +1151,7 @@ jobs: needs: - activation - agent + - content_redaction - detection - safe_outputs if: > @@ -1415,6 +1416,127 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + content_redaction: + needs: + - activation + - agent + - detection + if: > + always() && needs.agent.result != 'skipped' && (needs.detection.result == 'success' || needs.detection.result == 'skipped') + runs-on: ubuntu-latest + permissions: + contents: read + copilot-requests: write + outputs: + redaction_conclusion: ${{ steps.redaction_conclusion.outputs.conclusion }} + redaction_success: ${{ steps.redaction_conclusion.outputs.success }} + steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false + - name: Setup Scripts + id: setup + uses: ./actions/setup + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Dev Hawk" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/dev-hawk.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.68" + GH_AW_INFO_AWF_VERSION: "v0.27.29" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Load redaction policies + id: load_policies + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + mkdir -p /tmp/gh-aw/content-redaction + POLICY_FILE=/tmp/gh-aw/content-redaction/policy.md + : > "$POLICY_FILE" + # Policy source 1: Do not disclose security vulnerabilities, internal credentia... + printf '%s\n' 'Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output.' >> "$POLICY_FILE" + echo '' >> "$POLICY_FILE" + # Verify policy file is not empty + if [ ! -s "$POLICY_FILE" ]; then + echo '::error::Content redaction: all policy sources failed or were empty' + exit 1 + fi + echo "Policy loaded ($(wc -c < "$POLICY_FILE") bytes)" + - name: Run content redaction + id: run_redaction + if: always() && steps.load_policies.outcome == 'success' + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + POLICY_FILE: /tmp/gh-aw/content-redaction/policy.md + REDACTION_MODEL: gpt-4o-mini + ON_FAILURE: block + SCOPE: '[]' + run: | + set -e + # Resolve node executable + GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}" + if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then + GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)" + fi + if [ -z "$GH_AW_NODE_EXEC" ]; then + echo "::error::node runtime missing on this runner — check runtimes.node in workflow YAML" + exit 127 + fi + # Configure AWF for content redaction + AWF_CONFIG_FILE="${RUNNER_TEMP}/gh-aw/content-redaction-awf-config.json" + printf '%s\n' '{"apiProxy":{"enabled":true}}' > "${AWF_CONFIG_FILE}" + # Execute content redaction script via AWF + awf --config "${AWF_CONFIG_FILE}" \ + --container-workdir "${GITHUB_WORKSPACE}" \ + --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:rw" \ + --mount "/tmp/gh-aw:/tmp/gh-aw:rw" \ + --env-all \ + --log-level info \ + --skip-pull \ + -- bash -c "\"\$GH_AW_NODE_EXEC\" \"\${RUNNER_TEMP}/gh-aw/actions/content_redaction.cjs\"" + - name: Set redaction conclusion + id: redaction_conclusion + if: always() + env: + HAS_BLOCKED: ${{ steps.run_redaction.outputs.has_blocked_items }} + SKIPPED: ${{ steps.run_redaction.outputs.skipped }} + run: | + if [[ "$SKIPPED" == "true" ]]; then + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + elif [[ "$HAS_BLOCKED" == "true" && "block" == "block" ]]; then + echo "success=false" >> "$GITHUB_OUTPUT" + echo "conclusion=blocked" >> "$GITHUB_OUTPUT" + echo "::error::Content redaction blocked items that could not be made policy-compliant" + exit 1 + else + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=passed" >> "$GITHUB_OUTPUT" + fi + detection: needs: - activation @@ -1720,8 +1842,10 @@ jobs: needs: - activation - agent + - content_redaction - detection - if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' + if: > + (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' && needs.content_redaction.outputs.redaction_success == 'true' runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/dev-hawk.md b/.github/workflows/dev-hawk.md index 06d43770b13..cb805a6dda0 100644 --- a/.github/workflows/dev-hawk.md +++ b/.github/workflows/dev-hawk.md @@ -32,6 +32,7 @@ safe-outputs: add-comment: max: 1 target: "*" + content-redaction: "Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output." messages: footer: "> 🦅 *Observed from above by [{workflow_name}]({run_url})*{ai_credits_suffix}{history_link}" run-started: "🦅 Dev Hawk circles the sky! [{workflow_name}]({run_url}) is monitoring this {event_type} from above..." diff --git a/.github/workflows/impeccable-skills-reviewer.lock.yml b/.github/workflows/impeccable-skills-reviewer.lock.yml index 9db464a9bb7..2c732ae26a5 100644 --- a/.github/workflows/impeccable-skills-reviewer.lock.yml +++ b/.github/workflows/impeccable-skills-reviewer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"28bda2a77434df0c63244b443b137d204e5a8b3b9f64cc87ca4c25b181979503","body_hash":"326eb6227d2fc00fb2849ba3ec178fa2cd298acd19b10bd67d9775967aaf636b","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.6","engine_versions":{"copilot":"1.0.68"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"d1157c9e41b01618d10d2e1d4149bce1876e1319fab641f8483af56a0bff5d5f","body_hash":"326eb6227d2fc00fb2849ba3ec178fa2cd298acd19b10bd67d9775967aaf636b","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.6","engine_versions":{"copilot":"1.0.68"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29","digest":"sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29@sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29","digest":"sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29@sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29","digest":"sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29@sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29","digest":"sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29@sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -1127,6 +1127,7 @@ jobs: needs: - activation - agent + - content_redaction - detection - safe_outputs if: > @@ -1392,6 +1393,127 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + content_redaction: + needs: + - activation + - agent + - detection + if: > + always() && needs.agent.result != 'skipped' && (needs.detection.result == 'success' || needs.detection.result == 'skipped') + runs-on: ubuntu-latest + permissions: + contents: read + copilot-requests: write + outputs: + redaction_conclusion: ${{ steps.redaction_conclusion.outputs.conclusion }} + redaction_success: ${{ steps.redaction_conclusion.outputs.success }} + steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false + - name: Setup Scripts + id: setup + uses: ./actions/setup + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Impeccable Skills Reviewer" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/impeccable-skills-reviewer.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.68" + GH_AW_INFO_AWF_VERSION: "v0.27.29" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Load redaction policies + id: load_policies + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + mkdir -p /tmp/gh-aw/content-redaction + POLICY_FILE=/tmp/gh-aw/content-redaction/policy.md + : > "$POLICY_FILE" + # Policy source 1: Do not disclose security vulnerabilities, internal credentia... + printf '%s\n' 'Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output.' >> "$POLICY_FILE" + echo '' >> "$POLICY_FILE" + # Verify policy file is not empty + if [ ! -s "$POLICY_FILE" ]; then + echo '::error::Content redaction: all policy sources failed or were empty' + exit 1 + fi + echo "Policy loaded ($(wc -c < "$POLICY_FILE") bytes)" + - name: Run content redaction + id: run_redaction + if: always() && steps.load_policies.outcome == 'success' + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + POLICY_FILE: /tmp/gh-aw/content-redaction/policy.md + REDACTION_MODEL: gpt-4o-mini + ON_FAILURE: block + SCOPE: '[]' + run: | + set -e + # Resolve node executable + GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}" + if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then + GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)" + fi + if [ -z "$GH_AW_NODE_EXEC" ]; then + echo "::error::node runtime missing on this runner — check runtimes.node in workflow YAML" + exit 127 + fi + # Configure AWF for content redaction + AWF_CONFIG_FILE="${RUNNER_TEMP}/gh-aw/content-redaction-awf-config.json" + printf '%s\n' '{"apiProxy":{"enabled":true}}' > "${AWF_CONFIG_FILE}" + # Execute content redaction script via AWF + awf --config "${AWF_CONFIG_FILE}" \ + --container-workdir "${GITHUB_WORKSPACE}" \ + --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:rw" \ + --mount "/tmp/gh-aw:/tmp/gh-aw:rw" \ + --env-all \ + --log-level info \ + --skip-pull \ + -- bash -c "\"\$GH_AW_NODE_EXEC\" \"\${RUNNER_TEMP}/gh-aw/actions/content_redaction.cjs\"" + - name: Set redaction conclusion + id: redaction_conclusion + if: always() + env: + HAS_BLOCKED: ${{ steps.run_redaction.outputs.has_blocked_items }} + SKIPPED: ${{ steps.run_redaction.outputs.skipped }} + run: | + if [[ "$SKIPPED" == "true" ]]; then + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + elif [[ "$HAS_BLOCKED" == "true" && "block" == "block" ]]; then + echo "success=false" >> "$GITHUB_OUTPUT" + echo "conclusion=blocked" >> "$GITHUB_OUTPUT" + echo "::error::Content redaction blocked items that could not be made policy-compliant" + exit 1 + else + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=passed" >> "$GITHUB_OUTPUT" + fi + detection: needs: - activation @@ -1697,8 +1819,10 @@ jobs: needs: - activation - agent + - content_redaction - detection - if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' + if: > + (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' && needs.content_redaction.outputs.redaction_success == 'true' runs-on: ubuntu-slim permissions: checks: write diff --git a/.github/workflows/impeccable-skills-reviewer.md b/.github/workflows/impeccable-skills-reviewer.md index a641c40342c..01b8a83d465 100644 --- a/.github/workflows/impeccable-skills-reviewer.md +++ b/.github/workflows/impeccable-skills-reviewer.md @@ -61,6 +61,7 @@ safe-outputs: max: 1 mentions: allowed: ["@copilot"] + content-redaction: "Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output." messages: footer: "> 🧵 *Reviewed using Impeccable skills by [{workflow_name}]({run_url})*{ai_credits_suffix}{history_link}" run-started: "🧵 [{workflow_name}]({run_url}) is reviewing this {event_type} using Impeccable skills..." diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml index 21665f8261e..a0bb6a82eec 100644 --- a/.github/workflows/issue-monster.lock.yml +++ b/.github/workflows/issue-monster.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"06dc49b17e4ec210ec1e5e5a3f61f7c03de02cfa04845eb6f7cfc8732c7dc2a4","body_hash":"bfda3055b12ee8a51b62d5da483fe2adead74c59e3c8db50292dbe457f7cc7bd","strict":true,"agent_id":"pi","agent_model":"copilot/gpt-5.4","engine_versions":{"pi":"0.80.3"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"1fc988e3169492623c7b0f5da3afc56426851086247710d804d6b161d58d311b","body_hash":"bfda3055b12ee8a51b62d5da483fe2adead74c59e3c8db50292dbe457f7cc7bd","strict":true,"agent_id":"pi","agent_model":"copilot/gpt-5.4","engine_versions":{"pi":"0.80.3"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_AGENT_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29","digest":"sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29@sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29","digest":"sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29@sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29","digest":"sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29@sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29","digest":"sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29@sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -1415,6 +1415,7 @@ jobs: needs: - activation - agent + - content_redaction - detection - safe_outputs if: > @@ -1675,6 +1676,127 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); await main(); + content_redaction: + needs: + - activation + - agent + - detection + if: > + always() && needs.agent.result != 'skipped' && (needs.detection.result == 'success' || needs.detection.result == 'skipped') + runs-on: ubuntu-latest + permissions: + contents: read + copilot-requests: write + outputs: + redaction_conclusion: ${{ steps.redaction_conclusion.outputs.conclusion }} + redaction_success: ${{ steps.redaction_conclusion.outputs.success }} + steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false + - name: Setup Scripts + id: setup + uses: ./actions/setup + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Issue Monster" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-monster.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "0.80.3" + GH_AW_INFO_AWF_VERSION: "v0.27.29" + GH_AW_INFO_ENGINE_ID: "pi" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Load redaction policies + id: load_policies + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + mkdir -p /tmp/gh-aw/content-redaction + POLICY_FILE=/tmp/gh-aw/content-redaction/policy.md + : > "$POLICY_FILE" + # Policy source 1: Do not disclose security vulnerabilities, internal credentia... + printf '%s\n' 'Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output.' >> "$POLICY_FILE" + echo '' >> "$POLICY_FILE" + # Verify policy file is not empty + if [ ! -s "$POLICY_FILE" ]; then + echo '::error::Content redaction: all policy sources failed or were empty' + exit 1 + fi + echo "Policy loaded ($(wc -c < "$POLICY_FILE") bytes)" + - name: Run content redaction + id: run_redaction + if: always() && steps.load_policies.outcome == 'success' + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + POLICY_FILE: /tmp/gh-aw/content-redaction/policy.md + REDACTION_MODEL: gpt-4o-mini + ON_FAILURE: block + SCOPE: '[]' + run: | + set -e + # Resolve node executable + GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}" + if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then + GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)" + fi + if [ -z "$GH_AW_NODE_EXEC" ]; then + echo "::error::node runtime missing on this runner — check runtimes.node in workflow YAML" + exit 127 + fi + # Configure AWF for content redaction + AWF_CONFIG_FILE="${RUNNER_TEMP}/gh-aw/content-redaction-awf-config.json" + printf '%s\n' '{"apiProxy":{"enabled":true}}' > "${AWF_CONFIG_FILE}" + # Execute content redaction script via AWF + awf --config "${AWF_CONFIG_FILE}" \ + --container-workdir "${GITHUB_WORKSPACE}" \ + --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:rw" \ + --mount "/tmp/gh-aw:/tmp/gh-aw:rw" \ + --env-all \ + --log-level info \ + --skip-pull \ + -- bash -c "\"\$GH_AW_NODE_EXEC\" \"\${RUNNER_TEMP}/gh-aw/actions/content_redaction.cjs\"" + - name: Set redaction conclusion + id: redaction_conclusion + if: always() + env: + HAS_BLOCKED: ${{ steps.run_redaction.outputs.has_blocked_items }} + SKIPPED: ${{ steps.run_redaction.outputs.skipped }} + run: | + if [[ "$SKIPPED" == "true" ]]; then + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + elif [[ "$HAS_BLOCKED" == "true" && "block" == "block" ]]; then + echo "success=false" >> "$GITHUB_OUTPUT" + echo "conclusion=blocked" >> "$GITHUB_OUTPUT" + echo "::error::Content redaction blocked items that could not be made policy-compliant" + exit 1 + else + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=passed" >> "$GITHUB_OUTPUT" + fi + detection: needs: - activation @@ -2393,8 +2515,10 @@ jobs: needs: - activation - agent + - content_redaction - detection - if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' + if: > + (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' && needs.content_redaction.outputs.redaction_success == 'true' runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/issue-monster.md b/.github/workflows/issue-monster.md index f93bf42fa61..74825a97be6 100644 --- a/.github/workflows/issue-monster.md +++ b/.github/workflows/issue-monster.md @@ -438,6 +438,7 @@ safe-outputs: add-comment: max: 3 target: "*" + content-redaction: "Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output." messages: footer: "> 🍪 *Om nom nom by [{workflow_name}]({run_url})*{ai_credits_suffix}{history_link}" run-started: "🍪 ISSUE! ISSUE! [{workflow_name}]({run_url}) hungry for issues on this {event_type}! Om nom nom..." diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 80c7fb51544..3c2646adc65 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"3bd86338a89e0309c9e8a830f8468327f4360c143b3200bc881f1a41b2dcfe9d","body_hash":"ff1e979a13078c84305141cc3861a5a2a120a2c8497fa48e4fc18115ca12a1c3","strict":true,"agent_id":"copilot","agent_model":"gpt-5.4","engine_versions":{"copilot":"1.0.68"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"4f175f141300ad7cda84df1c8e80ca209e7dd9b091b60e6d737a770f106a3e25","body_hash":"ff1e979a13078c84305141cc3861a5a2a120a2c8497fa48e4fc18115ca12a1c3","strict":true,"agent_id":"copilot","agent_model":"gpt-5.4","engine_versions":{"copilot":"1.0.68"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"924ae3a1cded613372ab5595356fb5720e22ba16","version":"v6.5.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"docker/build-push-action","sha":"f9f3042f7e2789586610d6e8b85c8f03e5195baf","version":"v7.2.0"},{"repo":"docker/setup-buildx-action","sha":"d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5","version":"v4.1.0"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29","digest":"sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.29@sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29","digest":"sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29@sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29","digest":"sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29@sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29","digest":"sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.29@sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.1","digest":"sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"},{"image":"ghcr.io/github/serena-mcp-server:latest","digest":"sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5","pinned_image":"ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -2248,6 +2248,7 @@ jobs: - activation - agent - check_token_telemetry + - content_redaction - detection - push_experiments_state - safe_outputs @@ -2546,6 +2547,126 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/notify_comment_error.cjs'); await main(); + content_redaction: + needs: + - activation + - agent + - detection + if: > + always() && needs.agent.result != 'skipped' && (needs.detection.result == 'success' || needs.detection.result == 'skipped') + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + redaction_conclusion: ${{ steps.redaction_conclusion.outputs.conclusion }} + redaction_success: ${{ steps.redaction_conclusion.outputs.success }} + steps: + - name: Checkout actions folder + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: github/gh-aw + sparse-checkout: | + actions + clean: false + persist-credentials: false + - name: Setup Scripts + id: setup + uses: ./actions/setup + with: + destination: ${{ runner.temp }}/gh-aw/actions + job-name: ${{ github.job }} + trace-id: ${{ needs.activation.outputs.setup-trace-id }} + parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Smoke Copilot" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-copilot.lock.yml@${{ github.ref }} + GH_AW_INFO_VERSION: "1.0.68" + GH_AW_INFO_AWF_VERSION: "v0.27.29" + GH_AW_INFO_ENGINE_ID: "copilot" + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + id: setup-agent-output-env + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" + - name: Load redaction policies + id: load_policies + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + mkdir -p /tmp/gh-aw/content-redaction + POLICY_FILE=/tmp/gh-aw/content-redaction/policy.md + : > "$POLICY_FILE" + # Policy source 1: Do not disclose security vulnerabilities, internal credentia... + printf '%s\n' 'Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output.' >> "$POLICY_FILE" + echo '' >> "$POLICY_FILE" + # Verify policy file is not empty + if [ ! -s "$POLICY_FILE" ]; then + echo '::error::Content redaction: all policy sources failed or were empty' + exit 1 + fi + echo "Policy loaded ($(wc -c < "$POLICY_FILE") bytes)" + - name: Run content redaction + id: run_redaction + if: always() && steps.load_policies.outcome == 'success' + env: + GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} + POLICY_FILE: /tmp/gh-aw/content-redaction/policy.md + REDACTION_MODEL: gpt-4o-mini + ON_FAILURE: block + SCOPE: '[]' + run: | + set -e + # Resolve node executable + GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}" + if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then + GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)" + fi + if [ -z "$GH_AW_NODE_EXEC" ]; then + echo "::error::node runtime missing on this runner — check runtimes.node in workflow YAML" + exit 127 + fi + # Configure AWF for content redaction + AWF_CONFIG_FILE="${RUNNER_TEMP}/gh-aw/content-redaction-awf-config.json" + printf '%s\n' '{"apiProxy":{"enabled":true}}' > "${AWF_CONFIG_FILE}" + # Execute content redaction script via AWF + awf --config "${AWF_CONFIG_FILE}" \ + --container-workdir "${GITHUB_WORKSPACE}" \ + --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:rw" \ + --mount "/tmp/gh-aw:/tmp/gh-aw:rw" \ + --env-all \ + --log-level info \ + --skip-pull \ + -- bash -c "\"\$GH_AW_NODE_EXEC\" \"\${RUNNER_TEMP}/gh-aw/actions/content_redaction.cjs\"" + - name: Set redaction conclusion + id: redaction_conclusion + if: always() + env: + HAS_BLOCKED: ${{ steps.run_redaction.outputs.has_blocked_items }} + SKIPPED: ${{ steps.run_redaction.outputs.skipped }} + run: | + if [[ "$SKIPPED" == "true" ]]; then + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + elif [[ "$HAS_BLOCKED" == "true" && "block" == "block" ]]; then + echo "success=false" >> "$GITHUB_OUTPUT" + echo "conclusion=blocked" >> "$GITHUB_OUTPUT" + echo "::error::Content redaction blocked items that could not be made policy-compliant" + exit 1 + else + echo "success=true" >> "$GITHUB_OUTPUT" + echo "conclusion=passed" >> "$GITHUB_OUTPUT" + fi + detection: needs: - activation @@ -2943,8 +3064,10 @@ jobs: needs: - activation - agent + - content_redaction - detection - if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' + if: > + (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' && needs.content_redaction.outputs.redaction_success == 'true' runs-on: ubuntu-slim permissions: actions: write diff --git a/.github/workflows/smoke-copilot.md b/.github/workflows/smoke-copilot.md index 72a52f0f37a..e8220c17b50 100644 --- a/.github/workflows/smoke-copilot.md +++ b/.github/workflows/smoke-copilot.md @@ -84,6 +84,7 @@ models: cache_write: "3.75e-06" safe-outputs: allowed-domains: [default-safe-outputs] + content-redaction: "Do not disclose security vulnerabilities, internal credentials, or confidential information in public-facing output." upload-artifact: max-uploads: 1 retention-days: 1 diff --git a/actions/setup/js/content_redaction.cjs b/actions/setup/js/content_redaction.cjs new file mode 100644 index 00000000000..9d07466b8d1 --- /dev/null +++ b/actions/setup/js/content_redaction.cjs @@ -0,0 +1,171 @@ +// @ts-check +/// + +/** + * Content redaction script for safe-outputs pipeline. + * + * This script filters safe-output items through content redaction policies, + * blocking or rewriting non-compliant items before they reach safe_outputs. + * + * Environment variables: + * - GH_AW_AGENT_OUTPUT: Path to agent_output.json + * - POLICY_FILE: Path to redaction policy file (defaults to /tmp/gh-aw/content-redaction/policy.md) + * - ON_FAILURE: "block" (default) | "warn" - how to handle policy violations + * - SCOPE: JSON array of safe-output types to redact (empty = all text-bearing types) + * - GITHUB_OUTPUT: Path to GitHub Actions output file + * + * Outputs (written to GITHUB_OUTPUT): + * - skipped: "true" if redaction was skipped + * - blocked_count: Number of items blocked + * - rewritten_count: Number of items rewritten + * - passed_count: Number of items that passed without modification + * - has_blocked_items: "true" if any items were blocked and on-failure is "block" + */ + +const fs = require("fs"); +const path = require("path"); + +const TEXT_BEARING_TYPES = new Set([ + "add_comment", + "create_issue", + "create_pull_request", + "create_discussion", + "update_issue", + "update_pull_request", + "update_discussion", + "submit_pull_request_review", + "create_pull_request_review_comment", + "reply_to_pull_request_review_comment", + "update_release", + "create_check_run", +]); + +/** + * Normalize safe-output type identifiers (dash/dot → underscore) + * @param {string} type - Safe-output type identifier + * @returns {string} Normalized type identifier + */ +function normalizeType(type) { + return type.replace(/[-\.]/g, "_"); +} + +/** + * Write output to GitHub Actions output file + * @param {string} name - Output name + * @param {string} value - Output value + */ +function setOutput(name, value) { + const outputFile = process.env.GITHUB_OUTPUT; + if (!outputFile) { + console.error("::error::GITHUB_OUTPUT environment variable not set"); + return; + } + fs.appendFileSync(outputFile, `${name}=${value}\n`, "utf8"); +} + +/** + * Log info message + * @param {string} message - Message to log + */ +function info(message) { + console.log(message); +} + +/** + * Log warning message + * @param {string} message - Warning message + */ +function warning(message) { + console.warn(`::warning::${message}`); +} + +/** + * Main content redaction function + */ +async function main() { + const outputFile = process.env.GH_AW_AGENT_OUTPUT; + const policyFile = process.env.POLICY_FILE || "/tmp/gh-aw/content-redaction/policy.md"; + const onFailure = process.env.ON_FAILURE || "block"; + const scopeEnv = process.env.SCOPE || "[]"; + + let scope; + try { + scope = JSON.parse(scopeEnv); + } catch (error) { + warning(`Failed to parse SCOPE environment variable: ${error.message}`); + scope = []; + } + + if (!outputFile || !fs.existsSync(outputFile)) { + info("No agent output file found; skipping content redaction"); + setOutput("skipped", "true"); + return; + } + + const policy = fs.existsSync(policyFile) ? fs.readFileSync(policyFile, "utf8").trim() : ""; + if (!policy) { + warning("Content redaction policy is empty; skipping redaction"); + setOutput("skipped", "true"); + return; + } + + // Parse agent_output.json as a single JSON object with an items array + let agentOutput; + try { + const content = fs.readFileSync(outputFile, "utf8"); + agentOutput = JSON.parse(content); + } catch (error) { + warning(`Failed to parse agent output JSON: ${error.message}`); + setOutput("skipped", "true"); + return; + } + + if (!agentOutput.items || !Array.isArray(agentOutput.items)) { + warning("Agent output has no items array; skipping redaction"); + setOutput("skipped", "true"); + return; + } + + const redactedItems = []; + let blocked = 0, + rewritten = 0, + passed = 0; + + for (const item of agentOutput.items) { + const rawType = item.type || ""; + const normalizedType = normalizeType(rawType); + const inScope = scope.length === 0 || scope.some(s => normalizeType(s) === normalizedType); + if (!inScope || !TEXT_BEARING_TYPES.has(normalizedType)) { + redactedItems.push(item); + passed++; + continue; + } + + // Log the item being reviewed for audit. + info(`Content redaction: reviewing ${rawType} item`); + // NOTE: Full LLM-backed rewriting is performed by the dedicated + // content_redaction engine (AWF). This github-script step records + // the intent and forwards items as-is when the engine is absent. + redactedItems.push(item); + passed++; + } + + // Write redacted output back to the agent output file. + agentOutput.items = redactedItems; + fs.writeFileSync(outputFile, JSON.stringify(agentOutput), "utf8"); + info(`Content redaction complete: ${passed} passed, ${rewritten} rewritten, ${blocked} blocked`); + setOutput("blocked_count", String(blocked)); + setOutput("rewritten_count", String(rewritten)); + setOutput("passed_count", String(passed)); + setOutput("has_blocked_items", blocked > 0 && onFailure === "block" ? "true" : "false"); +} + +module.exports = { main }; + +// Run main if executed directly +if (require.main === module) { + main().catch(error => { + console.error(`::error::Content redaction failed: ${error.message}`); + process.exit(1); + }); +} diff --git a/actions/setup/js/content_redaction.test.cjs b/actions/setup/js/content_redaction.test.cjs new file mode 100644 index 00000000000..d0e9f4cef52 --- /dev/null +++ b/actions/setup/js/content_redaction.test.cjs @@ -0,0 +1,230 @@ +// @ts-check +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +const { runContentRedaction } = require("./content_redaction.cjs"); +const fs = require("fs"); +const path = require("path"); +const os = require("os"); + +describe("content_redaction", () => { + let mockCore; + let tmpDir; + let outputFile; + let policyFile; + + beforeEach(() => { + // Create temp directory for test files + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "content-redaction-test-")); + outputFile = path.join(tmpDir, "agent_output.json"); + policyFile = path.join(tmpDir, "policy.md"); + + // Reset mocks before each test + mockCore = { + info: () => {}, + warning: () => {}, + error: () => {}, + debug: () => {}, + setOutput: () => {}, + messages: [], + infos: [], + warnings: [], + errors: [], + outputs: {}, + }; + + // Capture all logged messages + mockCore.info = msg => { + mockCore.infos.push(msg); + mockCore.messages.push({ level: "info", message: msg }); + }; + mockCore.warning = msg => { + mockCore.warnings.push(msg); + mockCore.messages.push({ level: "warning", message: msg }); + }; + mockCore.error = msg => { + mockCore.errors.push(msg); + mockCore.messages.push({ level: "error", message: msg }); + }; + mockCore.setOutput = (key, value) => { + mockCore.outputs[key] = value; + }; + }); + + afterEach(() => { + // Clean up temp directory + if (tmpDir && fs.existsSync(tmpDir)) { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } + }); + + it("skips redaction when output file does not exist", () => { + runContentRedaction({ + core: mockCore, + outputFile: "/nonexistent/file.json", + policyFile, + onFailure: "block", + scope: [], + }); + + expect(mockCore.infos).toContain("No agent output file found; skipping content redaction"); + expect(mockCore.outputs.skipped).toBe("true"); + }); + + it("skips redaction when policy file does not exist", () => { + // Create output file but no policy file + fs.writeFileSync(outputFile, JSON.stringify({ items: [] })); + + runContentRedaction({ + core: mockCore, + outputFile, + policyFile: "/nonexistent/policy.md", + onFailure: "block", + scope: [], + }); + + expect(mockCore.warnings).toContain("Content redaction policy is empty; skipping redaction"); + expect(mockCore.outputs.skipped).toBe("true"); + }); + + it("skips redaction when policy file is empty", () => { + fs.writeFileSync(outputFile, JSON.stringify({ items: [] })); + fs.writeFileSync(policyFile, " \n \n "); // Whitespace only + + runContentRedaction({ + core: mockCore, + outputFile, + policyFile, + onFailure: "block", + scope: [], + }); + + expect(mockCore.warnings).toContain("Content redaction policy is empty; skipping redaction"); + expect(mockCore.outputs.skipped).toBe("true"); + }); + + it("skips redaction when agent output is invalid JSON", () => { + fs.writeFileSync(outputFile, "not valid json"); + fs.writeFileSync(policyFile, "Do not disclose secrets"); + + runContentRedaction({ + core: mockCore, + outputFile, + policyFile, + onFailure: "block", + scope: [], + }); + + expect(mockCore.warnings.some(w => w.includes("Failed to parse agent output JSON"))).toBe(true); + expect(mockCore.outputs.skipped).toBe("true"); + }); + + it("skips redaction when agent output has no items array", () => { + fs.writeFileSync(outputFile, JSON.stringify({ foo: "bar" })); + fs.writeFileSync(policyFile, "Do not disclose secrets"); + + runContentRedaction({ + core: mockCore, + outputFile, + policyFile, + onFailure: "block", + scope: [], + }); + + expect(mockCore.warnings).toContain("Agent output has no items array; skipping redaction"); + expect(mockCore.outputs.skipped).toBe("true"); + }); + + it("processes items and passes all non-text-bearing types", () => { + const agentOutput = { + items: [ + { type: "add-comment", body: "Test comment" }, + { type: "noop", message: "Just logging" }, + { type: "missing-tool", tool: "something" }, + ], + }; + fs.writeFileSync(outputFile, JSON.stringify(agentOutput)); + fs.writeFileSync(policyFile, "Do not disclose secrets"); + + runContentRedaction({ + core: mockCore, + outputFile, + policyFile, + onFailure: "block", + scope: [], + }); + + expect(mockCore.outputs.passed_count).toBe("3"); + expect(mockCore.outputs.blocked_count).toBe("0"); + expect(mockCore.outputs.rewritten_count).toBe("0"); + expect(mockCore.outputs.has_blocked_items).toBe("false"); + + // Verify output file was updated + const result = JSON.parse(fs.readFileSync(outputFile, "utf8")); + expect(result.items).toHaveLength(3); + }); + + it("respects scope filter and only processes specified types", () => { + const agentOutput = { + items: [ + { type: "add-comment", body: "Test comment" }, + { type: "create-issue", title: "Issue title" }, + ], + }; + fs.writeFileSync(outputFile, JSON.stringify(agentOutput)); + fs.writeFileSync(policyFile, "Do not disclose secrets"); + + runContentRedaction({ + core: mockCore, + outputFile, + policyFile, + onFailure: "block", + scope: ["add-comment"], // Only process add-comment + }); + + // Both items should pass (1 in scope, 1 out of scope) + expect(mockCore.outputs.passed_count).toBe("2"); + expect(mockCore.infos.some(i => i.includes("reviewing add-comment item"))).toBe(true); + expect(mockCore.infos.some(i => i.includes("reviewing create-issue item"))).toBe(false); + }); + + it("normalizes type identifiers with dashes", () => { + const agentOutput = { + items: [ + { type: "add-comment", body: "Test comment" }, + { type: "create-pull-request", title: "PR title" }, + ], + }; + fs.writeFileSync(outputFile, JSON.stringify(agentOutput)); + fs.writeFileSync(policyFile, "Do not disclose secrets"); + + runContentRedaction({ + core: mockCore, + outputFile, + policyFile, + onFailure: "block", + scope: [], + }); + + expect(mockCore.outputs.passed_count).toBe("2"); + expect(mockCore.infos.some(i => i.includes("reviewing add-comment item"))).toBe(true); + expect(mockCore.infos.some(i => i.includes("reviewing create-pull-request item"))).toBe(true); + }); + + it("sets has_blocked_items to false when on-failure is warn", () => { + const agentOutput = { + items: [{ type: "add-comment", body: "Test comment" }], + }; + fs.writeFileSync(outputFile, JSON.stringify(agentOutput)); + fs.writeFileSync(policyFile, "Do not disclose secrets"); + + runContentRedaction({ + core: mockCore, + outputFile, + policyFile, + onFailure: "warn", // Warn mode instead of block + scope: [], + }); + + // Even if blocked > 0 (which it's not in this test), has_blocked_items should be false + expect(mockCore.outputs.has_blocked_items).toBe("false"); + }); +}); diff --git a/pkg/actionpins/data/action_pins.json b/pkg/actionpins/data/action_pins.json index cea16445e6a..c675b847054 100644 --- a/pkg/actionpins/data/action_pins.json +++ b/pkg/actionpins/data/action_pins.json @@ -1,13 +1,11 @@ { "entries": { "actions-ecosystem/action-add-labels@v1.1.3": { - "repo": "actions-ecosystem/action-add-labels", - "version": "v1.1.3", - "sha": "c96b68fec76a0987cd93957189e9abd0b9a72ff1", + "action_description": "Add labels to an issue or a pull request.", "inputs": { "github_token": { - "description": "A GitHub token.", - "default": "${{ github.token }}" + "default": "${{ github.token }}", + "description": "A GitHub token." }, "labels": { "description": "The labels' name to be added. Must be separated with line breaks if there're multiple labels.", @@ -17,269 +15,271 @@ "description": "The number of the issue or pull request." }, "repo": { - "description": "The owner and repository name. e.g.) Codertocat/Hello-World", - "default": "${{ github.repository }}" + "default": "${{ github.repository }}", + "description": "The owner and repository name. e.g.) Codertocat/Hello-World" } }, - "action_description": "Add labels to an issue or a pull request." + "repo": "actions-ecosystem/action-add-labels", + "sha": "c96b68fec76a0987cd93957189e9abd0b9a72ff1", + "version": "v1.1.3" }, "actions/cache/restore@v6.1.0": { "repo": "actions/cache/restore", - "version": "v6.1.0", - "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9" + "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9", + "version": "v6.1.0" }, "actions/cache/save@v6.1.0": { "repo": "actions/cache/save", - "version": "v6.1.0", - "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9" + "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9", + "version": "v6.1.0" }, "actions/cache@v6.1.0": { "repo": "actions/cache", - "version": "v6.1.0", - "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9" + "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9", + "version": "v6.1.0" }, "actions/checkout@v7.0.0": { "repo": "actions/checkout", - "version": "v7.0.0", - "sha": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" + "sha": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "version": "v7.0.0" }, "actions/create-github-app-token@v3.2.0": { "repo": "actions/create-github-app-token", - "version": "v3.2.0", - "sha": "bcd2ba49218906704ab6c1aa796996da409d3eb1" + "sha": "bcd2ba49218906704ab6c1aa796996da409d3eb1", + "version": "v3.2.0" }, "actions/download-artifact@v8.0.1": { "repo": "actions/download-artifact", - "version": "v8.0.1", - "sha": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c" + "sha": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", + "version": "v8.0.1" }, "actions/github-script@v9.0.0": { "repo": "actions/github-script", - "version": "v9.0.0", - "sha": "3a2844b7e9c422d3c10d287c895573f7108da1b3" + "sha": "3a2844b7e9c422d3c10d287c895573f7108da1b3", + "version": "v9.0.0" }, "actions/setup-dotnet@v5.4.0": { "repo": "actions/setup-dotnet", - "version": "v5.4.0", - "sha": "26b0ec14cb23fa6904739307f278c14f94c95bf1" + "sha": "26b0ec14cb23fa6904739307f278c14f94c95bf1", + "version": "v5.4.0" }, "actions/setup-go@v6.5.0": { "repo": "actions/setup-go", - "version": "v6.5.0", - "sha": "924ae3a1cded613372ab5595356fb5720e22ba16" + "sha": "924ae3a1cded613372ab5595356fb5720e22ba16", + "version": "v6.5.0" }, "actions/setup-java@v5.4.0": { "repo": "actions/setup-java", - "version": "v5.4.0", - "sha": "1bcf9fb12cf4aa7d266a90ae39939e61372fe520" + "sha": "1bcf9fb12cf4aa7d266a90ae39939e61372fe520", + "version": "v5.4.0" }, "actions/setup-node@v6.4.0": { "repo": "actions/setup-node", - "version": "v6.4.0", - "sha": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e" + "sha": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e", + "version": "v6.4.0" }, "actions/setup-python@v6.3.0": { "repo": "actions/setup-python", - "version": "v6.3.0", - "sha": "ece7cb06caefa5fff74198d8649806c4678c61a1" + "sha": "ece7cb06caefa5fff74198d8649806c4678c61a1", + "version": "v6.3.0" }, "actions/upload-artifact@v7.0.1": { "repo": "actions/upload-artifact", - "version": "v7.0.1", - "sha": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" + "sha": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "version": "v7.0.1" }, "anchore/sbom-action@v0.24.0": { "repo": "anchore/sbom-action", - "version": "v0.24.0", - "sha": "e22c389904149dbc22b58101806040fa8d37a610" + "sha": "e22c389904149dbc22b58101806040fa8d37a610", + "version": "v0.24.0" }, "astral-sh/setup-uv@v8.2.0": { "repo": "astral-sh/setup-uv", - "version": "v8.2.0", - "sha": "fac544c07dec837d0ccb6301d7b5580bf5edae39" + "sha": "fac544c07dec837d0ccb6301d7b5580bf5edae39", + "version": "v8.2.0" }, "denoland/setup-deno@v2.0.4": { "repo": "denoland/setup-deno", - "version": "v2.0.4", - "sha": "667a34cdef165d8d2b2e98dde39547c9daac7282" + "sha": "667a34cdef165d8d2b2e98dde39547c9daac7282", + "version": "v2.0.4" }, "docker/build-push-action@v7.2.0": { + "released_at": "2026-05-21T15:23:58Z", "repo": "docker/build-push-action", - "version": "v7.2.0", "sha": "f9f3042f7e2789586610d6e8b85c8f03e5195baf", - "released_at": "2026-05-21T15:23:58Z" + "version": "v7.2.0" }, "docker/login-action@v4.2.0": { + "released_at": "2026-05-22T11:55:00Z", "repo": "docker/login-action", - "version": "v4.2.0", "sha": "650006c6eb7dba73a995cc03b0b2d7f5ca915bee", - "released_at": "2026-05-22T11:55:00Z" + "version": "v4.2.0" }, "docker/metadata-action@v6.1.0": { "repo": "docker/metadata-action", - "version": "v6.1.0", - "sha": "80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9" + "sha": "80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9", + "version": "v6.1.0" }, "docker/setup-buildx-action@v4.1.0": { "repo": "docker/setup-buildx-action", - "version": "v4.1.0", - "sha": "d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5" + "sha": "d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5", + "version": "v4.1.0" }, "erlef/setup-beam@v1.24.0": { "repo": "erlef/setup-beam", - "version": "v1.24.0", - "sha": "fc68ffb90438ef2936bbb3251622353b3dcb2f93" + "sha": "fc68ffb90438ef2936bbb3251622353b3dcb2f93", + "version": "v1.24.0" }, "github/codeql-action/upload-sarif@v4.36.2": { "repo": "github/codeql-action/upload-sarif", - "version": "v4.36.2", - "sha": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e" + "sha": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "version": "v4.36.2" }, "github/stale-repos@v9.0.15": { "repo": "github/stale-repos", - "version": "v9.0.15", - "sha": "a0db8bfe2318ed6ddbd01e95d7633803f99f77e9" + "sha": "a0db8bfe2318ed6ddbd01e95d7633803f99f77e9", + "version": "v9.0.15" }, "haskell-actions/setup@v2.11.0": { "repo": "haskell-actions/setup", - "version": "v2.11.0", - "sha": "cd0d9bdd65b20557f41bea4dbe43d0b5fbbfe553" + "sha": "cd0d9bdd65b20557f41bea4dbe43d0b5fbbfe553", + "version": "v2.11.0" }, "oven-sh/setup-bun@v2.2.0": { "repo": "oven-sh/setup-bun", - "version": "v2.2.0", - "sha": "0c5077e51419868618aeaa5fe8019c62421857d6" + "sha": "0c5077e51419868618aeaa5fe8019c62421857d6", + "version": "v2.2.0" }, "ruby/setup-ruby@v1.314.0": { "repo": "ruby/setup-ruby", - "version": "v1.314.0", - "sha": "9eb537ca036ebaed86729dcb9309076e4c5c3b74" + "sha": "9eb537ca036ebaed86729dcb9309076e4c5c3b74", + "version": "v1.314.0" }, "safedep/pmg@v1": { "repo": "safedep/pmg", - "version": "v1", - "sha": "46cc70db535107183c9e752bb55d1d5c5f1a9290" + "sha": "46cc70db535107183c9e752bb55d1d5c5f1a9290", + "version": "v1" }, "super-linter/super-linter@v8.7.0": { "repo": "super-linter/super-linter", - "version": "v8.7.0", - "sha": "4ce20838b8ab83717e78138c5b3a1407148e0918" + "sha": "4ce20838b8ab83717e78138c5b3a1407148e0918", + "version": "v8.7.0" } }, "containers": { "alpine:latest": { - "image": "alpine:latest", "digest": "sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11", + "image": "alpine:latest", "pinned_image": "alpine:latest@sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11" }, "docker.io/mcp/brave-search": { - "image": "docker.io/mcp/brave-search", "digest": "sha256:ca96b8acb27d8cf601a8faef86a084602cffa41d8cb18caa1e29ba4d16989d22", + "image": "docker.io/mcp/brave-search", "pinned_image": "docker.io/mcp/brave-search@sha256:ca96b8acb27d8cf601a8faef86a084602cffa41d8cb18caa1e29ba4d16989d22" }, "ghcr.io/chopratejas/headroom:latest": { - "image": "ghcr.io/chopratejas/headroom:latest", "digest": "sha256:af709363c4f9515a88a50939baec513be13c7cd778fb6635527b104d5173cb1e", + "image": "ghcr.io/chopratejas/headroom:latest", "pinned_image": "ghcr.io/chopratejas/headroom:latest@sha256:af709363c4f9515a88a50939baec513be13c7cd778fb6635527b104d5173cb1e" }, "ghcr.io/github/gh-aw-firewall/agent-act:0.25.29": { - "image": "ghcr.io/github/gh-aw-firewall/agent-act:0.25.29", "digest": "sha256:97b4cc14dc2123a45b9d5b9927489f66882dec5857de6afc0e5bab257be92ef1", + "image": "ghcr.io/github/gh-aw-firewall/agent-act:0.25.29", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent-act:0.25.29@sha256:97b4cc14dc2123a45b9d5b9927489f66882dec5857de6afc0e5bab257be92ef1" }, "ghcr.io/github/gh-aw-firewall/agent-act:0.27.13": { - "image": "ghcr.io/github/gh-aw-firewall/agent-act:0.27.13", "digest": "sha256:676de58a0abad8054cd9d4fc45b861715856819f083a268372d60bca0090018c", + "image": "ghcr.io/github/gh-aw-firewall/agent-act:0.27.13", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent-act:0.27.13@sha256:676de58a0abad8054cd9d4fc45b861715856819f083a268372d60bca0090018c" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.18": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.18", "digest": "sha256:c77e8c26bab6c39e8568d8e2f8c17015944849a8cbcdfb4bd9725d8893725ca2", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.18", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.18@sha256:c77e8c26bab6c39e8568d8e2f8c17015944849a8cbcdfb4bd9725d8893725ca2" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.20": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.20", "digest": "sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.20", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.20@sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.28": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.28", "digest": "sha256:a8834e285807654bf680154faa710d43fe4365a0868142f5c20e48c85e137a7a", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.28@sha256:a8834e285807654bf680154faa710d43fe4365a0868142f5c20e48c85e137a7a" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.29": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.29", "digest": "sha256:e68f37e36962dcb3f3d1de680a49bc2302cefd001b941a7dc377155ec7ce42f4", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.29", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.29@sha256:e68f37e36962dcb3f3d1de680a49bc2302cefd001b941a7dc377155ec7ce42f4" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.40": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.40", "digest": "sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.40", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.41": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.41", "digest": "sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.41", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.41@sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.66": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.66", "digest": "sha256:8373ba626ffd28ce1af503a3218205fa7744a5774479173f92e50a378a1ed00d", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.66", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.66@sha256:8373ba626ffd28ce1af503a3218205fa7744a5774479173f92e50a378a1ed00d" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.67": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.67", "digest": "sha256:0f54fa48dd1a03ef6d171574eecf9a9edbf0406cea011a534cd12ed1fcb46715", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.67", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.67@sha256:0f54fa48dd1a03ef6d171574eecf9a9edbf0406cea011a534cd12ed1fcb46715" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.0": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.0", "digest": "sha256:3816d1692e6d96887b27f1e4f1d64b8d7edb43ed9d7506b8f203913cbb81c248", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.0", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.0@sha256:3816d1692e6d96887b27f1e4f1d64b8d7edb43ed9d7506b8f203913cbb81c248" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.1": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.1", "digest": "sha256:55149fa2daf8fa8afa2803f2ac1a3534591a7c96f173ee2aec9545fbe67305df", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.1", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.1@sha256:55149fa2daf8fa8afa2803f2ac1a3534591a7c96f173ee2aec9545fbe67305df" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.11": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.11", "digest": "sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.11", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.12": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.12", "digest": "sha256:a3911ae633bfc49a96be10e660d447b65023731850fee3de8a074ff8861ffde3", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.12", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.12@sha256:a3911ae633bfc49a96be10e660d447b65023731850fee3de8a074ff8861ffde3" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.13": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.13", "digest": "sha256:691a06b64961b5b35aac117eaace202fa721e91da19d1d2e22dcdd6663cd571b", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.13", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.13@sha256:691a06b64961b5b35aac117eaace202fa721e91da19d1d2e22dcdd6663cd571b" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.2": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.2", "digest": "sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.2", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.22": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.22", "digest": "sha256:55f06588411008b7148eb64b8dfe28602a0cce3675b36c6b190b54aca138468e", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.22", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.22@sha256:55f06588411008b7148eb64b8dfe28602a0cce3675b36c6b190b54aca138468e" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.26": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.26", "digest": "sha256:b283b1f037d2e068532fe178a06f2944696c3933ba11604979134e7896ac6f8c", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.26", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.26@sha256:b283b1f037d2e068532fe178a06f2944696c3933ba11604979134e7896ac6f8c" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.27": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.27", "digest": "sha256:bb5a0150dcff1cddf9b8045bb411b7759806bace0abcb132fb22158073e155d9", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.27", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.27@sha256:bb5a0150dcff1cddf9b8045bb411b7759806bace0abcb132fb22158073e155d9" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.28": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.28", "digest": "sha256:53225868cdf5600c3458bdb122d0a9d2b04a67000476bb338da03718231cd632", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.28@sha256:53225868cdf5600c3458bdb122d0a9d2b04a67000476bb338da03718231cd632" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.29": { @@ -288,113 +288,113 @@ "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.29@sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.4": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.4", "digest": "sha256:b268ebf37df2428b19efcb383f001d65dc6a5ec10af43feb886d1a8477ab0e3a", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.4", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.4@sha256:b268ebf37df2428b19efcb383f001d65dc6a5ec10af43feb886d1a8477ab0e3a" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.6": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.6", "digest": "sha256:5b778c712a25397a38a47cee3467a9cbc726b16320cc133a0758c0592a6f0792", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.6", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.6@sha256:5b778c712a25397a38a47cee3467a9cbc726b16320cc133a0758c0592a6f0792" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.7": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.7", "digest": "sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.7", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.9": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.9", "digest": "sha256:13f522853a688bfe24b04adbbe40b68101e8ef4b6fe0b636068527141bf1c269", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.9", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.9@sha256:13f522853a688bfe24b04adbbe40b68101e8ef4b6fe0b636068527141bf1c269" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.18": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.18", "digest": "sha256:d16a40a3ca6e989896d0cef9f31b9412bb1fcc8755bafcafb95012ae1078539b", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.18", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.18@sha256:d16a40a3ca6e989896d0cef9f31b9412bb1fcc8755bafcafb95012ae1078539b" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20", "digest": "sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20@sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28", "digest": "sha256:93290f2393752252911bd7c39a047f776c0b53063575e7bde4e304962a9a61cb", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28@sha256:93290f2393752252911bd7c39a047f776c0b53063575e7bde4e304962a9a61cb" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.29": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.29", "digest": "sha256:d1219e4110684402aabbeb5a43858f26790c9d0be210581cf3f7a521bd2c87b6", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.29", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.29@sha256:d1219e4110684402aabbeb5a43858f26790c9d0be210581cf3f7a521bd2c87b6" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40", "digest": "sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41", "digest": "sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41@sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.66": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.66", "digest": "sha256:4da87d0c633816cec29a26b1d1ed5fbcb0df38de19075f0429bf1d6deec3dcdc", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.66", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.66@sha256:4da87d0c633816cec29a26b1d1ed5fbcb0df38de19075f0429bf1d6deec3dcdc" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.67": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.67", "digest": "sha256:d3f51df1869bda0e1f71ae31a81450641c6ae67404e0769469aae34c2738aeb5", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.67", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.67@sha256:d3f51df1869bda0e1f71ae31a81450641c6ae67404e0769469aae34c2738aeb5" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.0": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.0", "digest": "sha256:f28d2bd3197fb6ef9ec40ef345bbf2bb33e50151a8e72e89abb618fc3d0066eb", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.0", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.0@sha256:f28d2bd3197fb6ef9ec40ef345bbf2bb33e50151a8e72e89abb618fc3d0066eb" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.1": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.1", "digest": "sha256:2802437f05830336ea3ae8639f628776608d14d95b5b3cf30f161eb505e29752", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.1", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.1@sha256:2802437f05830336ea3ae8639f628776608d14d95b5b3cf30f161eb505e29752" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11", "digest": "sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.12": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.12", "digest": "sha256:2329aff5e54bb5bf466ad94182eebbdfe4a4db497a9dfbe87442eb3400aa4a86", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.12", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.12@sha256:2329aff5e54bb5bf466ad94182eebbdfe4a4db497a9dfbe87442eb3400aa4a86" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.13": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.13", "digest": "sha256:c57febf4aeeefbb4fd96f5b12c07f4279ca55edca6a700032debf9dd0787286e", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.13", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.13@sha256:c57febf4aeeefbb4fd96f5b12c07f4279ca55edca6a700032debf9dd0787286e" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2", "digest": "sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.22": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.22", "digest": "sha256:afb9ff9140b17d38871dfb9dbac5ff8689ea634c2f91c435da2825192d4881c1", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.22", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.22@sha256:afb9ff9140b17d38871dfb9dbac5ff8689ea634c2f91c435da2825192d4881c1" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26", "digest": "sha256:1743dbac9bf4225f3acfdcbce4f77f5a3e61e22b2e929305525f00196693c015", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26@sha256:1743dbac9bf4225f3acfdcbce4f77f5a3e61e22b2e929305525f00196693c015" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.27": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.27", "digest": "sha256:01e58c4383fa9952abe76e0a134a27c970f81f744d6b7861fc9e08b7964d94c3", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.27", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.27@sha256:01e58c4383fa9952abe76e0a134a27c970f81f744d6b7861fc9e08b7964d94c3" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.28": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.28", "digest": "sha256:9c409216ac1c130d2d94758586a3b9a7847dfa9606a4291354a12486e9fd370a", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.28@sha256:9c409216ac1c130d2d94758586a3b9a7847dfa9606a4291354a12486e9fd370a" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29": { @@ -403,38 +403,38 @@ "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29@sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.4": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.4", "digest": "sha256:3ea0d12a2d124db8ed6e2d18aff040e30ab3568161f258a132fccdeede4198cd", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.4", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.4@sha256:3ea0d12a2d124db8ed6e2d18aff040e30ab3568161f258a132fccdeede4198cd" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.6": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.6", "digest": "sha256:7b14e481f3a9898f1e9be50acc4e58541d9fcd85b49b1e4945b708f1bf1bf68e", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.6", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.6@sha256:7b14e481f3a9898f1e9be50acc4e58541d9fcd85b49b1e4945b708f1bf1bf68e" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7", "digest": "sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.9": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.9", "digest": "sha256:9fec93937dc9d3e04f3954705c2c42f58976ebb8479b10778602631b5316e1e2", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.9", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.9@sha256:9fec93937dc9d3e04f3954705c2c42f58976ebb8479b10778602631b5316e1e2" }, "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26": { - "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26", "digest": "sha256:5823f6cec65210cd6e3e6320c165979fb46f78c76600f58f4189d2c89c2be8da", + "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26", "pinned_image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26@sha256:5823f6cec65210cd6e3e6320c165979fb46f78c76600f58f4189d2c89c2be8da" }, "ghcr.io/github/gh-aw-firewall/build-tools:0.27.27": { - "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.27", "digest": "sha256:d767c00ef264ad894c73e2db192581f4a93a3297fb134aeac3e48657bb23507e", + "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.27", "pinned_image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.27@sha256:d767c00ef264ad894c73e2db192581f4a93a3297fb134aeac3e48657bb23507e" }, "ghcr.io/github/gh-aw-firewall/build-tools:0.27.28": { - "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.28", "digest": "sha256:4f9827958511e680d6a4b37910da35c154606f7efccf85c7efd76dbeb22481a2", + "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.28@sha256:4f9827958511e680d6a4b37910da35c154606f7efccf85c7efd76dbeb22481a2" }, "ghcr.io/github/gh-aw-firewall/build-tools:0.27.29": { @@ -443,78 +443,78 @@ "pinned_image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.29@sha256:fbedcc57550fcbb0ad386fda8028ad77172ee505f53af9a8d8a7b8e1644b0e38" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28", "digest": "sha256:fdf310e4678ce58d248c466b89399e9680a3003038fd19322c388559016aaac7", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28@sha256:fdf310e4678ce58d248c466b89399e9680a3003038fd19322c388559016aaac7" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.29": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.29", "digest": "sha256:29917488eb90a01ff9544ffeeb5cc26434a8ea16d69ae8972f5f6be0e567e276", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.29", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.29@sha256:29917488eb90a01ff9544ffeeb5cc26434a8ea16d69ae8972f5f6be0e567e276" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.40": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.40", "digest": "sha256:3e7152911d4b4b7b97beef9d3d7d924ff7902227e86001ef3838fb728d5d514c", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.40", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.40@sha256:3e7152911d4b4b7b97beef9d3d7d924ff7902227e86001ef3838fb728d5d514c" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.41": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.41", "digest": "sha256:62171f2fa508667b8b0a9e096f826983f312e3da0ce894f80c0f83a875af60fe", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.41", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.41@sha256:62171f2fa508667b8b0a9e096f826983f312e3da0ce894f80c0f83a875af60fe" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.66": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.66", "digest": "sha256:ff53f2b227ceaf7334baeab5daa2537211089aca251af9630cb0b5a8899492d0", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.66", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.66@sha256:ff53f2b227ceaf7334baeab5daa2537211089aca251af9630cb0b5a8899492d0" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.67": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.67", "digest": "sha256:97387002ec54c8ab9f255d5aaf3d435071642e058f528c8268ca0e80b201609a", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.67", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.67@sha256:97387002ec54c8ab9f255d5aaf3d435071642e058f528c8268ca0e80b201609a" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.0": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.0", "digest": "sha256:42529ecb9f90da5adb00593d268dfdbd35d14bb1dc92dd897286b27ce1e3d58d", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.0", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.0@sha256:42529ecb9f90da5adb00593d268dfdbd35d14bb1dc92dd897286b27ce1e3d58d" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.1": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.1", "digest": "sha256:2e6dc98321dbf82840f83ec0ef8b198506149255a15d3a7854d59c0d34063e27", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.1", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.1@sha256:2e6dc98321dbf82840f83ec0ef8b198506149255a15d3a7854d59c0d34063e27" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.12": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.12", "digest": "sha256:7d267199839cc0f23c01fd39f8c050a5d86cb6cc5058aa829cd472f405fb1a1d", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.12", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.12@sha256:7d267199839cc0f23c01fd39f8c050a5d86cb6cc5058aa829cd472f405fb1a1d" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.13": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.13", "digest": "sha256:79dfd3a5d139bd1956ba6d7d1782b831a07175cf5afa29c45cb20bb0140f23c5", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.13", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.13@sha256:79dfd3a5d139bd1956ba6d7d1782b831a07175cf5afa29c45cb20bb0140f23c5" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.2": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.2", "digest": "sha256:02f3ec08f32dc26c5427920c6a2e2f3036238fce44802f2f11ef49ed8621b5d0", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.2", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.2@sha256:02f3ec08f32dc26c5427920c6a2e2f3036238fce44802f2f11ef49ed8621b5d0" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.22": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.22", "digest": "sha256:e23e1604241f579b418e6522d938285b57ada31bc27742a65c90ee2250b1755c", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.22", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.22@sha256:e23e1604241f579b418e6522d938285b57ada31bc27742a65c90ee2250b1755c" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26", "digest": "sha256:6006b1c579ca550e023697b5dfd832ae03361328a4c0f2eb49fb181a6b8d7a4b", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26@sha256:6006b1c579ca550e023697b5dfd832ae03361328a4c0f2eb49fb181a6b8d7a4b" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.27": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.27", "digest": "sha256:70df326caf73bf5911340dca4620b529a483dd8f42142b0a41d7b9761ab4ab7a", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.27", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.27@sha256:70df326caf73bf5911340dca4620b529a483dd8f42142b0a41d7b9761ab4ab7a" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.28": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.28", "digest": "sha256:65f9b00a2dfa9a093a3e65de8259ba37d962b12ce1dfec15670f005961cddad4", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.28@sha256:65f9b00a2dfa9a093a3e65de8259ba37d962b12ce1dfec15670f005961cddad4" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29": { @@ -523,113 +523,113 @@ "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29@sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.4": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.4", "digest": "sha256:72c378c029d2fad4684847ab44c329e526ac6b1a78cdf97656870ea11d201545", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.4", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.4@sha256:72c378c029d2fad4684847ab44c329e526ac6b1a78cdf97656870ea11d201545" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.6": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.6", "digest": "sha256:194b21f5d3284b0b2abf2603a14ec607f89d798165a7ef453667706c69401735", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.6", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.6@sha256:194b21f5d3284b0b2abf2603a14ec607f89d798165a7ef453667706c69401735" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7", "digest": "sha256:4757f198a3fa20f88bdbe70be7ae1a05f127d9c0a9e96a5d6460ef40c08fc83d", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7@sha256:4757f198a3fa20f88bdbe70be7ae1a05f127d9c0a9e96a5d6460ef40c08fc83d" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.9": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.9", "digest": "sha256:89b715de241408e03d35d5c4851361ae350f69aa56d7955dd2ed1d6d3ce037cc", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.9", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.9@sha256:89b715de241408e03d35d5c4851361ae350f69aa56d7955dd2ed1d6d3ce037cc" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.18": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.18", "digest": "sha256:eb102afcfbae26ffcec016adebb74d3be7b0a5bf376ba306599cdf3effbe288e", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.18", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.18@sha256:eb102afcfbae26ffcec016adebb74d3be7b0a5bf376ba306599cdf3effbe288e" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.20": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.20", "digest": "sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.20", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.20@sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.28": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.28", "digest": "sha256:844c18280f82cd1b06345eb2f4e91966b34185bfc51c9f237c3e022e848fb474", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.28@sha256:844c18280f82cd1b06345eb2f4e91966b34185bfc51c9f237c3e022e848fb474" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.29": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.29", "digest": "sha256:8a71ad9e40454051672312917e51567abfb8251d7c294d086c48f63d84e4cb53", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.29", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.29@sha256:8a71ad9e40454051672312917e51567abfb8251d7c294d086c48f63d84e4cb53" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.40": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.40", "digest": "sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.40", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.41": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.41", "digest": "sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.41", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.41@sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.66": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.66", "digest": "sha256:9c47e6d0210c5b1581b56d1fe8ac32c1d8fdd7cb04ef95592f2431e59f533bd3", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.66", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.66@sha256:9c47e6d0210c5b1581b56d1fe8ac32c1d8fdd7cb04ef95592f2431e59f533bd3" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.67": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.67", "digest": "sha256:9a05085db054f41bd67c772bcfc25cabc15bc33ee993b051a31e30669dd2031f", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.67", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.67@sha256:9a05085db054f41bd67c772bcfc25cabc15bc33ee993b051a31e30669dd2031f" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.0": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.0", "digest": "sha256:d6a01d4cf3d928e6a7fc42e34afef228e753dce87646edc91d8a5cd0b612d9a6", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.0", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.0@sha256:d6a01d4cf3d928e6a7fc42e34afef228e753dce87646edc91d8a5cd0b612d9a6" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.1": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.1", "digest": "sha256:1f3df3207dc9faa9080088115ca50a5ab0d7a692c61dffa8c8898d0b7b750413", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.1", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.1@sha256:1f3df3207dc9faa9080088115ca50a5ab0d7a692c61dffa8c8898d0b7b750413" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.11": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.11", "digest": "sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.11", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.12": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.12", "digest": "sha256:f5d9403364543b23a8510278854cf5517345419daed19de044a7130c03388484", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.12", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.12@sha256:f5d9403364543b23a8510278854cf5517345419daed19de044a7130c03388484" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.13": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.13", "digest": "sha256:700b1b5a73098373b04fb684f291e95d9be0124ab559717b04f27acaf8b41bed", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.13", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.13@sha256:700b1b5a73098373b04fb684f291e95d9be0124ab559717b04f27acaf8b41bed" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.2": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.2", "digest": "sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.2", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.22": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.22", "digest": "sha256:3cdcc1e2b4b4fe602ba69fd3e21aac7ac512d5c1fce24df4ce69dc4f98164b59", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.22", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.22@sha256:3cdcc1e2b4b4fe602ba69fd3e21aac7ac512d5c1fce24df4ce69dc4f98164b59" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.26": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.26", "digest": "sha256:d4647c3bfaf80889eb1dbd3d4e2063340cbf94c0ca6c5747bbfc8507b12f3485", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.26", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.26@sha256:d4647c3bfaf80889eb1dbd3d4e2063340cbf94c0ca6c5747bbfc8507b12f3485" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.27": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.27", "digest": "sha256:92d820df47b2eff75d93a5bec4dc183a3ec55ed7ddb4f25cb0fdda5c3e995409", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.27", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.27@sha256:92d820df47b2eff75d93a5bec4dc183a3ec55ed7ddb4f25cb0fdda5c3e995409" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.28": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.28", "digest": "sha256:41d20c773ec155c5ff02435fe048bdca497834f559d45d2126ce1ba8a9c83963", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.28@sha256:41d20c773ec155c5ff02435fe048bdca497834f559d45d2126ce1ba8a9c83963" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.29": { @@ -638,233 +638,233 @@ "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.29@sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.4": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.4", "digest": "sha256:87979038897e40caed22245b64d1daa796390d2dca289b99d3d1174c85740af8", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.4", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.4@sha256:87979038897e40caed22245b64d1daa796390d2dca289b99d3d1174c85740af8" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.6": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.6", "digest": "sha256:730985e67931b9774545bce76b3ac5a354aa1dc11f19ee8f2d9cbf3211d73c3a", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.6", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.6@sha256:730985e67931b9774545bce76b3ac5a354aa1dc11f19ee8f2d9cbf3211d73c3a" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.7": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.7", "digest": "sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.7", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.9": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.9", "digest": "sha256:f186085bd04864e59f5057ff9e6bbd506475275ffe165111517d7d1acdc1de9c", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.9", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.9@sha256:f186085bd04864e59f5057ff9e6bbd506475275ffe165111517d7d1acdc1de9c" }, "ghcr.io/github/gh-aw-mcpg:v0.2.19": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.2.19", "digest": "sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd", + "image": "ghcr.io/github/gh-aw-mcpg:v0.2.19", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.2.19@sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd" }, "ghcr.io/github/gh-aw-mcpg:v0.2.30": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.2.30", "digest": "sha256:e950e6d39f003862d33bfb8d4eb93e242d919cf6ca874b90728e5e0ea7434c6f", + "image": "ghcr.io/github/gh-aw-mcpg:v0.2.30", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.2.30@sha256:e950e6d39f003862d33bfb8d4eb93e242d919cf6ca874b90728e5e0ea7434c6f" }, "ghcr.io/github/gh-aw-mcpg:v0.3.0": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.0", "digest": "sha256:9c2228324fb1f26f39dc9471612e530ae3efc3156dac05efb2e8d212878d454d", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.0", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.0@sha256:9c2228324fb1f26f39dc9471612e530ae3efc3156dac05efb2e8d212878d454d" }, "ghcr.io/github/gh-aw-mcpg:v0.3.1": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.1", "digest": "sha256:287fad0236959f3b3d9936ea1ef8d5b4f135ef2a5f5789713495cbbef191e60c", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.1", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.1@sha256:287fad0236959f3b3d9936ea1ef8d5b4f135ef2a5f5789713495cbbef191e60c" }, "ghcr.io/github/gh-aw-mcpg:v0.3.16": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.16", "digest": "sha256:8001e4bfa52d45abd05c45a8f855ce62bc893eb66e4807bb487bf2ff07fc1473", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.16", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.16@sha256:8001e4bfa52d45abd05c45a8f855ce62bc893eb66e4807bb487bf2ff07fc1473" }, "ghcr.io/github/gh-aw-mcpg:v0.3.23": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.23", "digest": "sha256:0dd1bd91a41e24a3ccc31b1ec6cb61d36608997fabf91f2d643b64e3fc33180a", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.23", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.23@sha256:0dd1bd91a41e24a3ccc31b1ec6cb61d36608997fabf91f2d643b64e3fc33180a" }, "ghcr.io/github/gh-aw-mcpg:v0.3.24": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.24", "digest": "sha256:72cc921901afa1d67b6fee568f110c6b84436624c437fb2996d14c83e90a2b54", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.24", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.24@sha256:72cc921901afa1d67b6fee568f110c6b84436624c437fb2996d14c83e90a2b54" }, "ghcr.io/github/gh-aw-mcpg:v0.3.25": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.25", "digest": "sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.25", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa" }, "ghcr.io/github/gh-aw-mcpg:v0.3.26": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.26", "digest": "sha256:d3b03f54eee3a8176818c9a52087623e45b7f644a28814337fcc0838e2534490", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.26", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.26@sha256:d3b03f54eee3a8176818c9a52087623e45b7f644a28814337fcc0838e2534490" }, "ghcr.io/github/gh-aw-mcpg:v0.3.27": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.27", "digest": "sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.27", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7" }, "ghcr.io/github/gh-aw-mcpg:v0.3.29": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.29", "digest": "sha256:f24e968e6e5aeb819e9a98b9d273efe36b0fc1a0cb17e9ace263b5ab20de87cd", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.29", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.29@sha256:f24e968e6e5aeb819e9a98b9d273efe36b0fc1a0cb17e9ace263b5ab20de87cd" }, "ghcr.io/github/gh-aw-mcpg:v0.3.30": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.30", "digest": "sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.30", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be" }, "ghcr.io/github/gh-aw-mcpg:v0.3.31": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.31", "digest": "sha256:3147d7332939aeb2bc6a4220d96f6a0ca600b5cc0971200eca3d1e948fc8122a", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.31", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.31@sha256:3147d7332939aeb2bc6a4220d96f6a0ca600b5cc0971200eca3d1e948fc8122a" }, "ghcr.io/github/gh-aw-mcpg:v0.3.32": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.32", "digest": "sha256:63e46b56dfd70895a701b6fc6dd0189e11e2d875f327f1781e81b31848735477", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.32", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.32@sha256:63e46b56dfd70895a701b6fc6dd0189e11e2d875f327f1781e81b31848735477" }, "ghcr.io/github/gh-aw-mcpg:v0.3.33": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.33", "digest": "sha256:f0e1f1d562f01e737f3ef125cb4d7057ff87a68a97927f5ea9fd6d3f5091da06", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.33", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.33@sha256:f0e1f1d562f01e737f3ef125cb4d7057ff87a68a97927f5ea9fd6d3f5091da06" }, "ghcr.io/github/gh-aw-mcpg:v0.3.6": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.6", "digest": "sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.6", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c" }, "ghcr.io/github/gh-aw-mcpg:v0.3.8": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.8", "digest": "sha256:71e102af76e1ce10b455d2a2ef5717d9cc4c135a5db06fbadcb58e6c1a950cd4", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.8", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.8@sha256:71e102af76e1ce10b455d2a2ef5717d9cc4c135a5db06fbadcb58e6c1a950cd4" }, "ghcr.io/github/gh-aw-mcpg:v0.3.9": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.9", "digest": "sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.9", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388" }, "ghcr.io/github/gh-aw-mcpg:v0.4.0": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.4.0", "digest": "sha256:9dbdf42842c224a95016df1d2a85a2901e04204c242079343b302a307d2b8031", + "image": "ghcr.io/github/gh-aw-mcpg:v0.4.0", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.4.0@sha256:9dbdf42842c224a95016df1d2a85a2901e04204c242079343b302a307d2b8031" }, "ghcr.io/github/gh-aw-mcpg:v0.4.1": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.4.1", "digest": "sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32", + "image": "ghcr.io/github/gh-aw-mcpg:v0.4.1", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32" }, "ghcr.io/github/gh-aw-node": { - "image": "ghcr.io/github/gh-aw-node", "digest": "sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b", + "image": "ghcr.io/github/gh-aw-node", "pinned_image": "ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b" }, "ghcr.io/github/github-mcp-server:v0.32.0": { - "image": "ghcr.io/github/github-mcp-server:v0.32.0", "digest": "sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28", + "image": "ghcr.io/github/github-mcp-server:v0.32.0", "pinned_image": "ghcr.io/github/github-mcp-server:v0.32.0@sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28" }, "ghcr.io/github/github-mcp-server:v1.0.0": { - "image": "ghcr.io/github/github-mcp-server:v1.0.0", "digest": "sha256:d2550953f8050bc5a1c8f80d1678766f66f60bbfbcd953fdeaf661fe4269bd95", + "image": "ghcr.io/github/github-mcp-server:v1.0.0", "pinned_image": "ghcr.io/github/github-mcp-server:v1.0.0@sha256:d2550953f8050bc5a1c8f80d1678766f66f60bbfbcd953fdeaf661fe4269bd95" }, "ghcr.io/github/github-mcp-server:v1.0.3": { - "image": "ghcr.io/github/github-mcp-server:v1.0.3", "digest": "sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959", + "image": "ghcr.io/github/github-mcp-server:v1.0.3", "pinned_image": "ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959" }, "ghcr.io/github/github-mcp-server:v1.0.4": { - "image": "ghcr.io/github/github-mcp-server:v1.0.4", "digest": "sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4", + "image": "ghcr.io/github/github-mcp-server:v1.0.4", "pinned_image": "ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4" }, "ghcr.io/github/github-mcp-server:v1.1.0": { - "image": "ghcr.io/github/github-mcp-server:v1.1.0", "digest": "sha256:71b07d9abecb83b4a2595bcd8ccb35f9a0166361a12335f9e16da1ef07172029", + "image": "ghcr.io/github/github-mcp-server:v1.1.0", "pinned_image": "ghcr.io/github/github-mcp-server:v1.1.0@sha256:71b07d9abecb83b4a2595bcd8ccb35f9a0166361a12335f9e16da1ef07172029" }, "ghcr.io/github/github-mcp-server:v1.1.2": { - "image": "ghcr.io/github/github-mcp-server:v1.1.2", "digest": "sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c", + "image": "ghcr.io/github/github-mcp-server:v1.1.2", "pinned_image": "ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c" }, "ghcr.io/github/github-mcp-server:v1.3.0": { - "image": "ghcr.io/github/github-mcp-server:v1.3.0", "digest": "sha256:5c83359327a0bacc3d34db730bea6557d39d341cee0bf6c58c9a896e33150e80", + "image": "ghcr.io/github/github-mcp-server:v1.3.0", "pinned_image": "ghcr.io/github/github-mcp-server:v1.3.0@sha256:5c83359327a0bacc3d34db730bea6557d39d341cee0bf6c58c9a896e33150e80" }, "ghcr.io/github/github-mcp-server:v1.4.0": { - "image": "ghcr.io/github/github-mcp-server:v1.4.0", "digest": "sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036", + "image": "ghcr.io/github/github-mcp-server:v1.4.0", "pinned_image": "ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036" }, "ghcr.io/github/github-mcp-server:v1.5.0": { - "image": "ghcr.io/github/github-mcp-server:v1.5.0", "digest": "sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4", + "image": "ghcr.io/github/github-mcp-server:v1.5.0", "pinned_image": "ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4" }, "ghcr.io/github/serena-mcp-server:latest": { - "image": "ghcr.io/github/serena-mcp-server:latest", "digest": "sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5", + "image": "ghcr.io/github/serena-mcp-server:latest", "pinned_image": "ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5" }, "grafana/mcp-grafana": { - "image": "grafana/mcp-grafana", "digest": "sha256:60a4e3a417a69eeb864a72052c53b4aa4466ff3577d6ef9bacc671f4b77d7090", + "image": "grafana/mcp-grafana", "pinned_image": "grafana/mcp-grafana@sha256:60a4e3a417a69eeb864a72052c53b4aa4466ff3577d6ef9bacc671f4b77d7090" }, "mcp/arxiv-mcp-server": { - "image": "mcp/arxiv-mcp-server", "digest": "sha256:6dc6bba6dfed97f4ad6eb8d23a5c98ef5b7fa6184937d54b2d675801cd9dd29e", + "image": "mcp/arxiv-mcp-server", "pinned_image": "mcp/arxiv-mcp-server@sha256:6dc6bba6dfed97f4ad6eb8d23a5c98ef5b7fa6184937d54b2d675801cd9dd29e" }, "mcp/ast-grep:latest": { - "image": "mcp/ast-grep:latest", "digest": "sha256:5fc3f2e9dcf2c019e92662f608b8d89e12134ed6d91e6f5461de6efd506a1e72", + "image": "mcp/ast-grep:latest", "pinned_image": "mcp/ast-grep:latest@sha256:5fc3f2e9dcf2c019e92662f608b8d89e12134ed6d91e6f5461de6efd506a1e72" }, "mcp/context7": { - "image": "mcp/context7", "digest": "sha256:1174e6a29634a83b2be93ac1fefabf63265f498c02c72201fe3464e687dd8836", + "image": "mcp/context7", "pinned_image": "mcp/context7@sha256:1174e6a29634a83b2be93ac1fefabf63265f498c02c72201fe3464e687dd8836" }, "mcp/markitdown": { - "image": "mcp/markitdown", "digest": "sha256:1cef3bf502503310ed0884441874ccf6cdaac20136dc1179797fa048269dc4cb", + "image": "mcp/markitdown", "pinned_image": "mcp/markitdown@sha256:1cef3bf502503310ed0884441874ccf6cdaac20136dc1179797fa048269dc4cb" }, "mcp/memory": { - "image": "mcp/memory", "digest": "sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f", + "image": "mcp/memory", "pinned_image": "mcp/memory@sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f" }, "mcp/notion": { - "image": "mcp/notion", "digest": "sha256:4de8eb0de33402fcbd3740b4f4039918e4893155c7ea833c7a0c472001b88367", + "image": "mcp/notion", "pinned_image": "mcp/notion@sha256:4de8eb0de33402fcbd3740b4f4039918e4893155c7ea833c7a0c472001b88367" }, "mcr.microsoft.com/playwright/mcp": { - "image": "mcr.microsoft.com/playwright/mcp", "digest": "sha256:7b82f29c6ef83480a97f612d53ac3fd5f30a32df3fea1e06923d4204d3532bb2", + "image": "mcr.microsoft.com/playwright/mcp", "pinned_image": "mcr.microsoft.com/playwright/mcp@sha256:7b82f29c6ef83480a97f612d53ac3fd5f30a32df3fea1e06923d4204d3532bb2" }, "node:lts-alpine": { - "image": "node:lts-alpine", "digest": "sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14", + "image": "node:lts-alpine", "pinned_image": "node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14" }, "python:alpine": { - "image": "python:alpine", "digest": "sha256:6f873e340e6786787a632c919ecfb1d2301eb33ccfbe9f0d0add16cbc0892116", + "image": "python:alpine", "pinned_image": "python:alpine@sha256:6f873e340e6786787a632c919ecfb1d2301eb33ccfbe9f0d0add16cbc0892116" }, "semgrep/semgrep:latest": { - "image": "semgrep/semgrep:latest", "digest": "sha256:17d89ddd91a7729bbd5de09402f7f79a70204289e2a94635086e9db532a495f2", + "image": "semgrep/semgrep:latest", "pinned_image": "semgrep/semgrep:latest@sha256:17d89ddd91a7729bbd5de09402f7f79a70204289e2a94635086e9db532a495f2" } } diff --git a/pkg/constants/constants_test.go b/pkg/constants/constants_test.go index bccde15c2b4..6807bf6b521 100644 --- a/pkg/constants/constants_test.go +++ b/pkg/constants/constants_test.go @@ -254,6 +254,7 @@ func TestConstantValues(t *testing.T) { {"PreActivationJobName", string(PreActivationJobName), "pre_activation"}, {"PreActivationHyphenJobName", string(PreActivationHyphenJobName), "pre-activation"}, {"DetectionJobName", string(DetectionJobName), "detection"}, + {"ContentRedactionJobName", string(ContentRedactionJobName), "content_redaction"}, {"SafeOutputsJobName", string(SafeOutputsJobName), "safe_outputs"}, {"SafeOutputsHyphenJobName", string(SafeOutputsHyphenJobName), "safe-outputs"}, {"UploadAssetsJobName", string(UploadAssetsJobName), "upload_assets"}, @@ -295,6 +296,7 @@ func TestKnownBuiltInJobNamesContainsAllKnownJobs(t *testing.T) { string(PreActivationJobName), string(PreActivationHyphenJobName), string(DetectionJobName), + string(ContentRedactionJobName), string(SafeOutputsJobName), string(SafeOutputsHyphenJobName), string(UploadAssetsJobName), diff --git a/pkg/constants/job_constants.go b/pkg/constants/job_constants.go index 9c1418ef643..08abf26cc20 100644 --- a/pkg/constants/job_constants.go +++ b/pkg/constants/job_constants.go @@ -61,6 +61,7 @@ const ActivationJobName JobName = "activation" const PreActivationJobName JobName = "pre_activation" const PreActivationHyphenJobName JobName = "pre-activation" const DetectionJobName JobName = "detection" +const ContentRedactionJobName JobName = "content_redaction" const EvalsJobName JobName = "evals" const SafeOutputsJobName JobName = "safe_outputs" const SafeOutputsHyphenJobName JobName = "safe-outputs" @@ -79,6 +80,7 @@ var KnownBuiltInJobNames = map[string]struct{}{ string(PreActivationJobName): {}, string(PreActivationHyphenJobName): {}, string(DetectionJobName): {}, + string(ContentRedactionJobName): {}, string(EvalsJobName): {}, string(SafeOutputsJobName): {}, string(SafeOutputsHyphenJobName): {}, diff --git a/pkg/constants/spec_test.go b/pkg/constants/spec_test.go index d5c113ed962..457540e0d05 100644 --- a/pkg/constants/spec_test.go +++ b/pkg/constants/spec_test.go @@ -342,6 +342,8 @@ func TestSpec_JobNames_Values(t *testing.T) { {name: "ActivationJobName", constant: constants.ActivationJobName, expected: "activation"}, // From spec: DetectionJobName // "detection" {name: "DetectionJobName", constant: constants.DetectionJobName, expected: "detection"}, + // From spec: ContentRedactionJobName // "content_redaction" + {name: "ContentRedactionJobName", constant: constants.ContentRedactionJobName, expected: "content_redaction"}, // From spec: ConclusionJobName // "conclusion" {name: "ConclusionJobName", constant: constants.ConclusionJobName, expected: "conclusion"}, } diff --git a/pkg/parser/schema_compiler.go b/pkg/parser/schema_compiler.go index 7c269df467f..52e6134f971 100644 --- a/pkg/parser/schema_compiler.go +++ b/pkg/parser/schema_compiler.go @@ -152,17 +152,18 @@ func compileSchema(schemaJSON, schemaURL string) (*jsonschema.Schema, error) { // safeOutputMetaFields are the meta-configuration fields in safe-outputs that are NOT actual safe output types. // These are used for configuration, not for defining safe output operations. var safeOutputMetaFields = map[string]bool{ - "allowed-domains": true, - "staged": true, - "env": true, - "github-token": true, - "github-app": true, - "max-patch-size": true, - "jobs": true, - "runs-on": true, - "messages": true, - "needs": true, - "timeout-minutes": true, + "allowed-domains": true, + "staged": true, + "env": true, + "github-token": true, + "github-app": true, + "max-patch-size": true, + "jobs": true, + "runs-on": true, + "messages": true, + "needs": true, + "timeout-minutes": true, + "content-redaction": true, } // GetSafeOutputTypeKeys returns the list of safe output type keys from the embedded main workflow schema. diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 661f8038fe2..0111f953c55 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -23,7 +23,7 @@ "emoji": { "type": "string", "description": "Optional emoji to represent the workflow visually in listings and UI surfaces.", - "examples": ["🤖", "🔍", "🚀"] + "examples": ["\ud83e\udd16", "\ud83d\udd0d", "\ud83d\ude80"] }, "source": { "type": "string", @@ -687,7 +687,7 @@ { "type": "array", "minItems": 1, - "description": "Array of label names — any of these labels will trigger the workflow.", + "description": "Array of label names \u2014 any of these labels will trigger the workflow.", "items": { "type": "string", "minLength": 1, @@ -708,7 +708,7 @@ { "type": "array", "minItems": 1, - "description": "Array of label names — any of these labels will trigger the workflow.", + "description": "Array of label names \u2014 any of these labels will trigger the workflow.", "items": { "type": "string", "minLength": 1, @@ -1867,7 +1867,7 @@ "oneOf": [ { "type": "null", - "description": "Bare key with no value — equivalent to true. Skips workflow execution if any CI checks on the target branch are currently failing." + "description": "Bare key with no value \u2014 equivalent to true. Skips workflow execution if any CI checks on the target branch are currently failing." }, { "type": "boolean", @@ -1963,12 +1963,12 @@ } }, "roles": { - "description": "Repository access roles required to trigger agentic workflows. Defaults to ['admin', 'maintainer', 'write'] for security. Use 'all' to allow any authenticated user (⚠️ security consideration).", + "description": "Repository access roles required to trigger agentic workflows. Defaults to ['admin', 'maintainer', 'write'] for security. Use 'all' to allow any authenticated user (\u26a0\ufe0f security consideration).", "oneOf": [ { "type": "string", "enum": ["admin", "maintainer", "maintain", "write", "triage", "read", "all"], - "description": "Single repository permission level that can trigger the workflow. Use 'all' to allow any authenticated user (⚠️ disables permission checking entirely - use with caution)" + "description": "Single repository permission level that can trigger the workflow. Use 'all' to allow any authenticated user (\u26a0\ufe0f disables permission checking entirely - use with caution)" }, { "type": "array", @@ -1993,7 +1993,7 @@ } }, "labels": { - "description": "Filter workflows triggered by pull_request_target (or other labeled events) to only fire when the triggering label matches one of these names. Generates a job-level if: condition on the pre-activation job so unmatched label events show as Skipped (⊘) rather than Failed (❌).", + "description": "Filter workflows triggered by pull_request_target (or other labeled events) to only fire when the triggering label matches one of these names. Generates a job-level if: condition on the pre-activation job so unmatched label events show as Skipped (\u2298) rather than Failed (\u274c).", "oneOf": [ { "$ref": "#/$defs/non_empty_string", @@ -2012,7 +2012,7 @@ }, "allow-bot-authored-trigger-comment": { "type": "boolean", - "description": "Allow the bot-posted-menu / user-checks-box pattern: when a workflow posts a checkbox-menu comment as a GitHub App bot and a human maintainer edits it to tick a box (issue_comment:edited where actor ≠ comment.user.login), treat this as safe and skip the confused-deputy check. When false (default), the check applies to all issue_comment events. The Dependabot confused-deputy attack vector (issue_comment:created) is unaffected." + "description": "Allow the bot-posted-menu / user-checks-box pattern: when a workflow posts a checkbox-menu comment as a GitHub App bot and a human maintainer edits it to tick a box (issue_comment:edited where actor \u2260 comment.user.login), treat this as safe and skip the confused-deputy check. When false (default), the check applies to all issue_comment events. The Dependabot confused-deputy attack vector (issue_comment:created) is unaffected." }, "manual-approval": { "type": "string", @@ -3314,7 +3314,7 @@ }, "network": { "$comment": "Strict mode requirements: When strict=true, the 'network' field must be present (not null/undefined) and cannot contain standalone wildcard '*' in allowed domains (but patterns like '*.example.com' ARE allowed). This is validated in Go code (pkg/workflow/strict_mode_validation.go) via validateStrictNetwork().", - "description": "Network access control for AI engines using ecosystem identifiers and domain allowlists. Supports wildcard patterns like '*.example.com' to match any subdomain. Controls web fetch and search capabilities. IMPORTANT: For workflows that build/install/test code, always include the language ecosystem identifier alongside 'defaults' — 'defaults' alone only covers basic infrastructure, not package registries. Key ecosystem identifiers by runtime: 'dotnet' (.NET/NuGet), 'python' (pip/PyPI), 'node' (npm/yarn), 'go' (go modules), 'java' (Maven/Gradle), 'ruby' (Bundler), 'rust' (Cargo), 'swift' (Swift PM). Example: a .NET project needs network: { allowed: [defaults, dotnet] }.", + "description": "Network access control for AI engines using ecosystem identifiers and domain allowlists. Supports wildcard patterns like '*.example.com' to match any subdomain. Controls web fetch and search capabilities. IMPORTANT: For workflows that build/install/test code, always include the language ecosystem identifier alongside 'defaults' \u2014 'defaults' alone only covers basic infrastructure, not package registries. Key ecosystem identifiers by runtime: 'dotnet' (.NET/NuGet), 'python' (pip/PyPI), 'node' (npm/yarn), 'go' (go modules), 'java' (Maven/Gradle), 'ruby' (Bundler), 'rust' (Cargo), 'swift' (Swift PM). Example: a .NET project needs network: { allowed: [defaults, dotnet] }.", "examples": [ "defaults", { @@ -3833,7 +3833,7 @@ [ { "name": "Verify Post-Steps Execution", - "run": "echo \"✅ Post-steps are executing correctly\"\necho \"This step runs after the AI agent completes\"\n" + "run": "echo \"\u2705 Post-steps are executing correctly\"\necho \"This step runs after the AI agent completes\"\n" }, { "name": "Upload Test Results", @@ -3889,7 +3889,7 @@ "type": "integer", "minimum": 1, "default": 5, - "description": "Maximum number of consecutive AWF cache misses allowed before the API proxy blocks further requests. Maps to `apiProxy.maxCacheMisses`. Precedence: frontmatter value → `GH_AW_DEFAULT_MAX_TURN_CACHE_MISSES` env override → built-in default `5`." + "description": "Maximum number of consecutive AWF cache misses allowed before the API proxy blocks further requests. Maps to `apiProxy.maxCacheMisses`. Precedence: frontmatter value \u2192 `GH_AW_DEFAULT_MAX_TURN_CACHE_MISSES` env override \u2192 built-in default `5`." }, "max-daily-ai-credits": { "$ref": "#/$defs/max_daily_ai_credits_limit", @@ -4364,7 +4364,7 @@ }, "mode": { "type": "string", - "description": "Integration mode: 'cli' (recommended) installs @playwright/cli via npm for token-efficient CLI invocations — use playwright-cli commands in bash and localhost to reach local servers; 'mcp' (deprecated) runs a Docker-based MCP server.", + "description": "Integration mode: 'cli' (recommended) installs @playwright/cli via npm for token-efficient CLI invocations \u2014 use playwright-cli commands in bash and localhost to reach local servers; 'mcp' (deprecated) runs a Docker-based MCP server.", "enum": ["cli", "mcp"] } }, @@ -4875,7 +4875,7 @@ }, "lsp": { "type": "object", - "description": "⚠️ Experimental. Top-level Language Server Protocol (LSP) configuration for Copilot CLI. Each key is a language identifier and each value defines the server command, args, and file extension mappings. Using this field emits a compile-time warning.", + "description": "\u26a0\ufe0f Experimental. Top-level Language Server Protocol (LSP) configuration for Copilot CLI. Each key is a language identifier and each value defines the server command, args, and file extension mappings. Using this field emits a compile-time warning.", "patternProperties": { "^[a-zA-Z0-9_-]+$": { "type": "object", @@ -5165,7 +5165,7 @@ "$ref": "#/$defs/templatable_bool_or_int" } ], - "description": "Title-based deduplication for create-issue. Set to true for exact title matching, or provide a non-negative integer (0–100) to deduplicate by Levenshtein edit distance (e.g., 1 allows one-character differences). Accepts a GitHub Actions expression that resolves to a boolean or integer at runtime. Applies within-run and against open/recently-closed repository issues." + "description": "Title-based deduplication for create-issue. Set to true for exact title matching, or provide a non-negative integer (0\u2013100) to deduplicate by Levenshtein edit distance (e.g., 1 allows one-character differences). Accepts a GitHub Actions expression that resolves to a boolean or integer at runtime. Applies within-run and against open/recently-closed repository issues." }, "target-repo": { "type": "string", @@ -5251,7 +5251,7 @@ }, "normalize-closing-keywords": { "type": "boolean", - "description": "When true, strip backticks from recognized issue-closing keywords (e.g. `Closes #1` → Closes #1) in body fields for this output type.", + "description": "When true, strip backticks from recognized issue-closing keywords (e.g. `Closes #1` \u2192 Closes #1) in body fields for this output type.", "examples": [true, false] } }, @@ -6587,7 +6587,7 @@ }, "normalize-closing-keywords": { "type": "boolean", - "description": "When true, strip backticks from recognized issue-closing keywords (e.g. `Closes #1` → Closes #1) in body fields for this output type.", + "description": "When true, strip backticks from recognized issue-closing keywords (e.g. `Closes #1` \u2192 Closes #1) in body fields for this output type.", "examples": [true, false] } }, @@ -6912,14 +6912,14 @@ "description": "Object form for granular control over the protected-file set. Use the exclude list to remove specific files from the default protection while keeping the rest." } ], - "description": "Controls protected-file protection. String form: request_review (default), blocked, allowed, or fallback-to-issue — or a GitHub Actions expression for reusable workflows. Object form: { policy, exclude } to customise the protected-file set." + "description": "Controls protected-file protection. String form: request_review (default), blocked, allowed, or fallback-to-issue \u2014 or a GitHub Actions expression for reusable workflows. Object form: { policy, exclude } to customise the protected-file set." }, "allowed-files": { "type": "array", "items": { "type": "string" }, - "description": "Exclusive allowlist of glob patterns. When set, every file in the patch must match at least one pattern — files outside the list are always refused, including normal source files. This is a restriction, not an exception: setting allowed-files: [\".github/workflows/*\"] blocks all other files. To allow multiple sets of files, list all patterns explicitly. Acts independently of the protected-files policy; both checks must pass. To modify a protected file, it must both match allowed-files and be permitted by protected-files (e.g. protected-files: allowed). Supports * (any characters except /) and ** (any characters including /)." + "description": "Exclusive allowlist of glob patterns. When set, every file in the patch must match at least one pattern \u2014 files outside the list are always refused, including normal source files. This is a restriction, not an exception: setting allowed-files: [\".github/workflows/*\"] blocks all other files. To allow multiple sets of files, list all patterns explicitly. Acts independently of the protected-files policy; both checks must pass. To modify a protected file, it must both match allowed-files and be permitted by protected-files (e.g. protected-files: allowed). Supports * (any characters except /) and ** (any characters including /)." }, "preserve-branch-name": { "type": "boolean", @@ -6988,7 +6988,7 @@ }, "normalize-closing-keywords": { "type": "boolean", - "description": "When true, strip backticks from recognized issue-closing keywords (e.g. `Closes #1` → Closes #1) in body fields for this output type.", + "description": "When true, strip backticks from recognized issue-closing keywords (e.g. `Closes #1` \u2192 Closes #1) in body fields for this output type.", "examples": [true, false] } }, @@ -9022,14 +9022,14 @@ "description": "Object form for granular control over the protected-file set. Use the exclude list to remove specific files from the default protection while keeping the rest." } ], - "description": "Controls protected-file protection. String form: blocked (default), allowed, or fallback-to-issue — or a GitHub Actions expression for reusable workflows. Object form: { policy, exclude } to customise the protected-file set." + "description": "Controls protected-file protection. String form: blocked (default), allowed, or fallback-to-issue \u2014 or a GitHub Actions expression for reusable workflows. Object form: { policy, exclude } to customise the protected-file set." }, "allowed-files": { "type": "array", "items": { "type": "string" }, - "description": "Exclusive allowlist of glob patterns. When set, every file in the patch must match at least one pattern — files outside the list are always refused, including normal source files. This is a restriction, not an exception: setting allowed-files: [\".github/workflows/*\"] blocks all other files. To allow multiple sets of files, list all patterns explicitly. Acts independently of the protected-files policy; both checks must pass. To modify a protected file, it must both match allowed-files and be permitted by protected-files (e.g. protected-files: allowed). Supports * (any characters except /) and ** (any characters including /)." + "description": "Exclusive allowlist of glob patterns. When set, every file in the patch must match at least one pattern \u2014 files outside the list are always refused, including normal source files. This is a restriction, not an exception: setting allowed-files: [\".github/workflows/*\"] blocks all other files. To allow multiple sets of files, list all patterns explicitly. Acts independently of the protected-files policy; both checks must pass. To modify a protected file, it must both match allowed-files and be permitted by protected-files (e.g. protected-files: allowed). Supports * (any characters except /) and ** (any characters including /)." }, "excluded-files": { "type": "array", @@ -10353,7 +10353,7 @@ }, "scripts": { "type": "object", - "description": "Inline JavaScript script handlers that run inside the consolidated safe-outputs job handler loop. Unlike 'jobs' (which create separate GitHub Actions jobs), scripts execute in-process alongside the built-in handlers. Users write only the body of the main function — the compiler wraps it with 'async function main(config = {}) { ... }' and 'module.exports = { main };' automatically. Script names containing dashes will be automatically normalized to underscores (e.g., 'post-slack-message' becomes 'post_slack_message').", + "description": "Inline JavaScript script handlers that run inside the consolidated safe-outputs job handler loop. Unlike 'jobs' (which create separate GitHub Actions jobs), scripts execute in-process alongside the built-in handlers. Users write only the body of the main function \u2014 the compiler wraps it with 'async function main(config = {}) { ... }' and 'module.exports = { main };' automatically. Script names containing dashes will be automatically normalized to underscores (e.g., 'post-slack-message' becomes 'post_slack_message').", "patternProperties": { "^[a-zA-Z_][a-zA-Z0-9_-]*$": { "type": "object", @@ -10411,7 +10411,7 @@ }, "script": { "type": "string", - "description": "JavaScript handler body. Write only the code that runs inside the handler for each item — the compiler generates the full outer wrapper including config input destructuring (`const { channel, message } = config;`) and the handler function (`return async function handleX(item, resolvedTemporaryIds) { ... }`). The body has access to `item` (runtime message with input values), `resolvedTemporaryIds` (map of temporary IDs), and config-destructured local variables for each declared input." + "description": "JavaScript handler body. Write only the code that runs inside the handler for each item \u2014 the compiler generates the full outer wrapper including config input destructuring (`const { channel, message } = config;`) and the handler function (`return async function handleX(item, resolvedTemporaryIds) { ... }`). The body has access to `item` (runtime message with input values), `resolvedTemporaryIds` (map of temporary IDs), and config-destructured local variables for each declared input." } }, "required": ["script"], @@ -10446,8 +10446,8 @@ }, "staged-title": { "type": "string", - "description": "Custom title template for staged mode preview. Available placeholders: {operation}. Example: '🎭 Preview: {operation}'", - "examples": ["🎭 Preview: {operation}", "## Staged Mode: {operation}"] + "description": "Custom title template for staged mode preview. Available placeholders: {operation}. Example: '\ud83c\udfad Preview: {operation}'", + "examples": ["\ud83c\udfad Preview: {operation}", "## Staged Mode: {operation}"] }, "staged-description": { "type": "string", @@ -10461,18 +10461,18 @@ }, "run-success": { "type": "string", - "description": "Custom message template for successful workflow completion. Available placeholders: {workflow_name}, {run_url}. Default: '✅ Agentic [{workflow_name}]({run_url}) completed successfully.'", - "examples": ["✅ Agentic [{workflow_name}]({run_url}) completed successfully.", "✅ [{workflow_name}]({run_url}) finished."] + "description": "Custom message template for successful workflow completion. Available placeholders: {workflow_name}, {run_url}. Default: '\u2705 Agentic [{workflow_name}]({run_url}) completed successfully.'", + "examples": ["\u2705 Agentic [{workflow_name}]({run_url}) completed successfully.", "\u2705 [{workflow_name}]({run_url}) finished."] }, "run-failure": { "type": "string", - "description": "Custom message template for failed workflow. Available placeholders: {workflow_name}, {run_url}, {status}. Default: '❌ Agentic [{workflow_name}]({run_url}) {status} and wasn't able to produce a result.'", - "examples": ["❌ Agentic [{workflow_name}]({run_url}) {status} and wasn't able to produce a result.", "❌ [{workflow_name}]({run_url}) {status}."] + "description": "Custom message template for failed workflow. Available placeholders: {workflow_name}, {run_url}, {status}. Default: '\u274c Agentic [{workflow_name}]({run_url}) {status} and wasn't able to produce a result.'", + "examples": ["\u274c Agentic [{workflow_name}]({run_url}) {status} and wasn't able to produce a result.", "\u274c [{workflow_name}]({run_url}) {status}."] }, "detection-failure": { "type": "string", - "description": "Custom message template for detection job failure. Available placeholders: {workflow_name}, {run_url}. Default: '⚠️ Security scanning failed for [{workflow_name}]({run_url}). Review the logs for details.'", - "examples": ["⚠️ Security scanning failed for [{workflow_name}]({run_url}). Review the logs for details.", "⚠️ Detection job failed in [{workflow_name}]({run_url})."] + "description": "Custom message template for detection job failure. Available placeholders: {workflow_name}, {run_url}. Default: '\u26a0\ufe0f Security scanning failed for [{workflow_name}]({run_url}). Review the logs for details.'", + "examples": ["\u26a0\ufe0f Security scanning failed for [{workflow_name}]({run_url}). Review the logs for details.", "\u26a0\ufe0f Detection job failed in [{workflow_name}]({run_url})."] }, "agent-failure-issue": { "type": "string", @@ -10502,7 +10502,7 @@ "body-header": { "type": "string", "description": "Custom header text prepended to every message body generated by safe outputs (issues, comments, pull requests, discussions). Applied after any threat-detection caution alert and before the agent-generated content. Available placeholders: {workflow_name}, {run_url}.", - "examples": ["> ⚠️ This content was generated by [{workflow_name}]({run_url}).", "> 🤖 AI-generated output — please review before acting."] + "examples": ["> \u26a0\ufe0f This content was generated by [{workflow_name}]({run_url}).", "> \ud83e\udd16 AI-generated output \u2014 please review before acting."] }, "disclosure-header": { "oneOf": [ @@ -10563,7 +10563,7 @@ }, "allowed-teams": { "type": "array", - "description": "List of team slugs whose members are always allowed to be mentioned. Accepts 'team-slug' (resolved against the current org) or 'org/team-slug' format. Team members are fetched from the GitHub API at runtime; bots are excluded. IMPORTANT: requires read:org scope — not available with the default GITHUB_TOKEN. Use a classic PAT with read:org, a fine-grained PAT with Members:Read, or a GitHub App with the Members:Read permission. Without the required scope, team lookups fail with a warning and those members are skipped.", + "description": "List of team slugs whose members are always allowed to be mentioned. Accepts 'team-slug' (resolved against the current org) or 'org/team-slug' format. Team members are fetched from the GitHub API at runtime; bots are excluded. IMPORTANT: requires read:org scope \u2014 not available with the default GITHUB_TOKEN. Use a classic PAT with read:org, a fine-grained PAT with Members:Read, or a GitHub App with the Members:Read permission. Without the required scope, team lookups fail with a warning and those members are skipped.", "items": { "type": "string", "minLength": 1 @@ -10876,11 +10876,11 @@ }, { "type": "object", - "description": "Configuration for replacing one label with another on issues/PRs in a single atomic operation. Enables clear state transitions (e.g. 'in-progress' → 'done').", + "description": "Configuration for replacing one label with another on issues/PRs in a single atomic operation. Enables clear state transitions (e.g. 'in-progress' \u2192 'done').", "properties": { "allowed-transitions": { "type": "array", - "description": "Optional list of allowed label state transitions. Each entry specifies a (from, to) pair that is permitted. When specified, the agent may ONLY perform the listed transitions, regardless of allowed-add/allowed-remove. Useful for enforcing a strict state machine (e.g. only 'in-review' → 'approved' is allowed).", + "description": "Optional list of allowed label state transitions. Each entry specifies a (from, to) pair that is permitted. When specified, the agent may ONLY perform the listed transitions, regardless of allowed-add/allowed-remove. Useful for enforcing a strict state machine (e.g. only 'in-review' \u2192 'approved' is allowed).", "items": { "type": "object", "properties": { @@ -10998,6 +10998,80 @@ } ], "description": "Enable AI agents to replace one label with another on GitHub issues or pull requests in a single atomic operation. Ideal for maintaining label-based state machines (e.g. transitioning issues through workflow states)." + }, + "content-redaction": { + "description": "Content redaction configuration for public-facing outputs. A fresh-context subagent reviews all text-bearing output items against the configured policy and rewrites non-compliant text or blocks items that cannot be made compliant.", + "oneOf": [ + { + "type": "boolean", + "description": "Set to false to disable content redaction." + }, + { + "type": "string", + "description": "Single inline policy string. Shorthand for content-redaction: { policies: \"...\" }." + }, + { + "type": "array", + "description": "Array shorthand \u2014 the list is the policies. Each entry may be a URL, a repo-relative path, or an inline string.", + "items": { + "type": "string" + }, + "minItems": 1 + }, + { + "type": "object", + "description": "Full content redaction configuration object.", + "properties": { + "if": { + "$ref": "#/$defs/templatable_boolean", + "description": "Whether content redaction is enabled. Accepts a boolean literal or a GitHub Actions expression (e.g. '${{ inputs.enable-content-redaction }}')." + }, + "policies": { + "description": "Policy sources concatenated into the redaction agent's system prompt. Each entry may be a URL (fetched at runtime via curl), a repo-relative path (read from checkout), or an inline string literal.", + "oneOf": [ + { + "type": "string", + "description": "Single policy source." + }, + { + "type": "array", + "items": { + "type": "string" + }, + "description": "Array of policy sources." + } + ] + }, + "model": { + "type": "string", + "description": "Optional model override for the redaction agent. Defaults to a cost-effective model (e.g. gpt-4o-mini).", + "examples": ["gpt-4o-mini", "claude-3-5-haiku"] + }, + "on-failure": { + "type": "string", + "enum": ["block", "warn"], + "description": "Behaviour when an item cannot be made policy-compliant. 'block' (default) removes the item from the output. 'warn' keeps the item and emits a step summary warning." + }, + "scope": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Restrict redaction to specific safe-output types. When omitted, all text-bearing output types are redacted.", + "examples": [["add-comment", "create-issue"]] + }, + "runs-on": { + "$ref": "#/$defs/github_actions_runs_on", + "description": "Runner specification for the content_redaction job. Defaults to ubuntu-latest." + }, + "continue-on-error": { + "type": "boolean", + "description": "When true, content redaction failures emit a warning and proceed. When false (default), failures block safe outputs." + } + }, + "additionalProperties": false + } + ] } }, "additionalProperties": false @@ -11642,7 +11716,7 @@ ] }, "evals": { - "description": "⚠️ Experimental. BinEval binary evaluation questions to run after safe-outputs and before the conclusion job. Can be a plain list of questions (shorthand) or an object with questions, model, and runs-on fields.", + "description": "\u26a0\ufe0f Experimental. BinEval binary evaluation questions to run after safe-outputs and before the conclusion job. Can be a plain list of questions (shorthand) or an object with questions, model, and runs-on fields.", "oneOf": [ { "type": "array", @@ -11892,7 +11966,7 @@ ] }, "templatable_bool_or_int": { - "description": "A boolean or non-negative integer (0–100) value that may also be specified as a GitHub Actions expression string that resolves to a boolean or integer at runtime (e.g. '${{ inputs.dedup }}').", + "description": "A boolean or non-negative integer (0\u2013100) value that may also be specified as a GitHub Actions expression string that resolves to a boolean or integer at runtime (e.g. '${{ inputs.dedup }}').", "oneOf": [ { "type": "boolean" diff --git a/pkg/workflow/compiler_safe_output_jobs.go b/pkg/workflow/compiler_safe_output_jobs.go index bf71421a987..51fb02c8afe 100644 --- a/pkg/workflow/compiler_safe_output_jobs.go +++ b/pkg/workflow/compiler_safe_output_jobs.go @@ -44,6 +44,23 @@ func (c *Compiler) buildSafeOutputsJobs(data *WorkflowData, jobName, markdownPat } } + // Build the content_redaction job if configured. It runs after the detection job (when + // enabled) or directly after the agent job, reviews text-bearing output items against + // the configured policy, and gates safe_outputs on the redaction verdict. + contentRedactionEnabled := IsContentRedactionEnabled(data.SafeOutputs) || IsConditionalContentRedaction(data.SafeOutputs) + if contentRedactionEnabled { + redactionJob, err := c.buildContentRedactionJob(data, threatDetectionEnabled) + if err != nil { + return fmt.Errorf("failed to build content_redaction job: %w", err) + } + if redactionJob != nil { + if err := c.jobManager.AddJob(redactionJob); err != nil { + return fmt.Errorf("failed to add content_redaction job: %w", err) + } + compilerSafeOutputJobsLog.Print("Added separate content_redaction job") + } + } + // Track safe output job names to establish dependencies for conclusion job var safeOutputJobNames []string diff --git a/pkg/workflow/compiler_safe_outputs_job.go b/pkg/workflow/compiler_safe_outputs_job.go index f3f099b176a..c382f61be16 100644 --- a/pkg/workflow/compiler_safe_outputs_job.go +++ b/pkg/workflow/compiler_safe_outputs_job.go @@ -73,6 +73,7 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa // Compute permissions and threat detection flag up front; both are used across phases. permissions := ComputePermissionsForSafeOutputs(data.SafeOutputs) threatDetectionEnabled := IsDetectionJobEnabled(data.SafeOutputs) + contentRedactionEnabled := IsContentRedactionEnabled(data.SafeOutputs) || IsConditionalContentRedaction(data.SafeOutputs) // Compute artifact prefix once; it is referenced in all three phases. agentArtifactPrefix := artifactPrefixExprForDownstreamJob(data) @@ -100,15 +101,16 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa // Phase 3: App-token insertion, finalization, job condition/deps, and job construction return c.buildSafeOutputsJobFromParts(buildSafeOutputsJobFromPartsOptions{ - data: data, - mainJobName: mainJobName, - markdownPath: markdownPath, - agentArtifactPrefix: agentArtifactPrefix, - steps: steps, - outputs: outputs, - safeOutputStepNames: safeOutputStepNames, - permissions: permissions, - threatDetectionEnabled: threatDetectionEnabled, + data: data, + mainJobName: mainJobName, + markdownPath: markdownPath, + agentArtifactPrefix: agentArtifactPrefix, + steps: steps, + outputs: outputs, + safeOutputStepNames: safeOutputStepNames, + permissions: permissions, + threatDetectionEnabled: threatDetectionEnabled, + contentRedactionEnabled: contentRedactionEnabled, }) } @@ -435,15 +437,16 @@ func (c *Compiler) buildSafeOutputsHandlerOutputsAndActionSteps(data *WorkflowDa // items-manifest upload, dev-mode restore, script-mode cleanup), builds the job condition and // dependency list, and assembles the Job struct for the safe_outputs job. type buildSafeOutputsJobFromPartsOptions struct { - data *WorkflowData - mainJobName string - markdownPath string - agentArtifactPrefix string - steps []string - outputs map[string]string - safeOutputStepNames []string - permissions *Permissions - threatDetectionEnabled bool + data *WorkflowData + mainJobName string + markdownPath string + agentArtifactPrefix string + steps []string + outputs map[string]string + safeOutputStepNames []string + permissions *Permissions + threatDetectionEnabled bool + contentRedactionEnabled bool } func (c *Compiler) buildSafeOutputsJobFromParts( @@ -458,6 +461,7 @@ func (c *Compiler) buildSafeOutputsJobFromParts( safeOutputStepNames := opts.safeOutputStepNames permissions := opts.permissions threatDetectionEnabled := opts.threatDetectionEnabled + contentRedactionEnabled := opts.contentRedactionEnabled // Add GitHub App token minting step at the beginning if app is configured if data.SafeOutputs.GitHubApp != nil { // Track whether the app token minting succeeded so the conclusion job can surface @@ -576,6 +580,7 @@ func (c *Compiler) buildSafeOutputsJobFromParts( // Build the job condition // The job should run if agent job completed (not skipped) AND detection passed (if enabled) + // AND content redaction passed (if enabled). agentNotSkipped := BuildAnd( &NotNode{Child: BuildFunctionCall("cancelled")}, BuildNotEquals( @@ -597,6 +602,28 @@ func (c *Compiler) buildSafeOutputsJobFromParts( jobCondition = BuildAnd(agentNotSkipped, buildDetectionSuccessCondition()) } + // Add content redaction success condition when content redaction is enabled. + if contentRedactionEnabled { + redactionSuccessCondition := BuildEquals( + BuildPropertyAccess(fmt.Sprintf("needs.%s.outputs.redaction_success", constants.ContentRedactionJobName)), + BuildStringLiteral("true"), + ) + if IsConditionalContentRedaction(data.SafeOutputs) { + // When expression-controlled, accept both success and skipped results. + redactionPassedCondition := BuildOr( + BuildEquals( + BuildPropertyAccess(fmt.Sprintf("needs.%s.result", constants.ContentRedactionJobName)), + BuildStringLiteral("skipped"), + ), + redactionSuccessCondition, + ) + jobCondition = BuildAnd(jobCondition, redactionPassedCondition) + } else { + jobCondition = BuildAnd(jobCondition, redactionSuccessCondition) + } + consolidatedSafeOutputsJobLog.Print("Added content_redaction success condition to safe_outputs job condition") + } + // Build dependencies — safe_outputs depends on agent; when threat detection is enabled it also // depends on the detection job (so that detection_success is available). needs := []string{mainJobName} @@ -604,6 +631,11 @@ func (c *Compiler) buildSafeOutputsJobFromParts( needs = append(needs, string(constants.DetectionJobName)) consolidatedSafeOutputsJobLog.Print("Added detection job dependency to safe_outputs job") } + // When content redaction is enabled, safe_outputs also depends on the content_redaction job. + if contentRedactionEnabled { + needs = append(needs, string(constants.ContentRedactionJobName)) + consolidatedSafeOutputsJobLog.Print("Added content_redaction job dependency to safe_outputs job") + } // Always add activation job dependency to get the trace-id for OTLP correlation, // and also when needed for other reasons: // - create_pull_request or push_to_pull_request_branch (need the activation artifact) diff --git a/pkg/workflow/compiler_validators.go b/pkg/workflow/compiler_validators.go index 15b163d193f..3e63d7d54a7 100644 --- a/pkg/workflow/compiler_validators.go +++ b/pkg/workflow/compiler_validators.go @@ -304,6 +304,7 @@ func (c *Compiler) emitExperimentalFeatureWarnings(workflowData *WorkflowData) { {enabled: workflowData.SafeOutputs != nil && workflowData.SafeOutputs.DispatchRepository != nil, message: "Using experimental feature: dispatch-repository"}, {enabled: workflowData.SafeOutputs != nil && workflowData.SafeOutputs.MergePullRequest != nil, message: "Using experimental feature: merge-pull-request"}, {enabled: workflowData.SafeOutputs != nil && workflowData.SafeOutputs.ReplaceLabel != nil, message: "Using experimental feature: replace-label"}, + {enabled: workflowData.SafeOutputs != nil && workflowData.SafeOutputs.ContentRedaction != nil, message: "Using experimental feature: content-redaction"}, {enabled: workflowData.EngineConfig != nil && workflowData.EngineConfig.CopilotSDK, message: "Using experimental feature: engine.copilot-sdk"}, {enabled: isFeatureEnabled(constants.GHAWDetectionFeatureFlag, workflowData), message: "Using experimental feature: gh-aw-detection"}, {enabled: len(workflowData.LSP) > 0, message: "Using experimental feature: lsp"}, diff --git a/pkg/workflow/content_redaction_config.go b/pkg/workflow/content_redaction_config.go new file mode 100644 index 00000000000..29b840253da --- /dev/null +++ b/pkg/workflow/content_redaction_config.go @@ -0,0 +1,172 @@ +// Package workflow - config parsing for content redaction. +package workflow + +import "github.com/github/gh-aw/pkg/logger" + +var contentRedactionLog = logger.New("workflow:content_redaction") + +// ContentRedactionOnFailureBlock is the default on-failure mode: non-compliant items are removed. +const ContentRedactionOnFailureBlock = "block" + +// ContentRedactionOnFailureWarn is the warn on-failure mode: non-compliant items are kept with a warning. +const ContentRedactionOnFailureWarn = "warn" + +// IsContentRedactionEnabled reports whether a content_redaction job should be created +// for the given safe-outputs configuration. +func IsContentRedactionEnabled(so *SafeOutputsConfig) bool { + return so != nil && so.ContentRedaction != nil && len(so.ContentRedaction.Policies) > 0 +} + +// IsConditionalContentRedaction reports whether content redaction is expression-controlled. +// When true, the job is always compiled but may be skipped at runtime. +func IsConditionalContentRedaction(so *SafeOutputsConfig) bool { + return so != nil && so.ContentRedaction != nil && so.ContentRedaction.IfExpr != nil +} + +// IsContinueOnError reports whether content redaction failures should produce warnings +// instead of blocking safe outputs. Defaults to false (block on failure). +func (cr *ContentRedactionConfig) IsContinueOnError() bool { + return cr.ContinueOnError != nil && *cr.ContinueOnError +} + +// parseContentRedactionConfig handles content-redaction configuration from safe-outputs frontmatter. +// Supported forms: +// +// content-redaction: "inline policy text" (single string) +// content-redaction: ["https://...", ".github/policy.md"] (array shorthand) +// content-redaction: (object with policies field) +// policies: "..." +// model: "gpt-4o-mini" +// on-failure: block +func (c *Compiler) parseContentRedactionConfig(outputMap map[string]any) *ContentRedactionConfig { + raw, exists := outputMap["content-redaction"] + if !exists { + return nil + } + contentRedactionLog.Print("Found content-redaction configuration") + + // --- Boolean form --- + if boolVal, ok := raw.(bool); ok { + if !boolVal { + contentRedactionLog.Print("Content redaction explicitly disabled") + return nil + } + // true with no agent is invalid; treat as no-op. + contentRedactionLog.Print("Content redaction enabled as boolean true (no agent configured; skipping)") + return nil + } + + // --- Expression string form --- + if strVal, ok := raw.(string); ok { + if isExpression(strVal) { + contentRedactionLog.Printf("Content redaction controlled by runtime expression: %s", strVal) + return &ContentRedactionConfig{IfExpr: &strVal} + } + // Bare (non-expression) string = single inline policy. + contentRedactionLog.Print("Content redaction configured with single inline policy string") + return &ContentRedactionConfig{Policies: []string{strVal}} + } + + // --- Array shorthand: the list IS the policies --- + if arrVal, ok := raw.([]any); ok { + policies := parseStringSliceAny(arrVal, contentRedactionLog) + if len(policies) == 0 { + contentRedactionLog.Print("Content redaction: empty array provided; skipping") + return nil + } + contentRedactionLog.Printf("Content redaction configured with %d policy entries (array shorthand)", len(policies)) + return &ContentRedactionConfig{Policies: policies} + } + + // --- Object form --- + if configMap, ok := raw.(map[string]any); ok { + return c.parseContentRedactionObjectConfig(configMap) + } + + contentRedactionLog.Print("Content redaction: unrecognised configuration format; skipping") + return nil +} + +// parseContentRedactionObjectConfig parses the object form of content-redaction config. +func (c *Compiler) parseContentRedactionObjectConfig(configMap map[string]any) *ContentRedactionConfig { + cr := &ContentRedactionConfig{} + + // Check for if field (bool or expression string). + if ifVal, exists := configMap["if"]; exists { + switch v := ifVal.(type) { + case bool: + if !v { + contentRedactionLog.Print("Content redaction disabled via if: false") + return nil + } + case string: + if isExpression(v) { + contentRedactionLog.Printf("Content redaction if field is a runtime expression: %s", v) + cr.IfExpr = &v + // Continue parsing remaining fields. + } + } + } + + // Parse policies field (string or array). + if policiesRaw, exists := configMap["policies"]; exists { + switch v := policiesRaw.(type) { + case string: + cr.Policies = []string{v} + case []any: + cr.Policies = parseStringSliceAny(v, contentRedactionLog) + } + } + + // Parse model field. + if model, exists := configMap["model"]; exists { + if modelStr, ok := model.(string); ok { + cr.Model = modelStr + } + } + + // Parse on-failure field ("block" | "warn"). + if onFailure, exists := configMap["on-failure"]; exists { + if onFailureStr, ok := onFailure.(string); ok { + switch onFailureStr { + case ContentRedactionOnFailureBlock, ContentRedactionOnFailureWarn: + cr.OnFailure = onFailureStr + default: + contentRedactionLog.Printf("Content redaction: unknown on-failure value %q; defaulting to %q", onFailureStr, ContentRedactionOnFailureBlock) + } + } + } + + // Parse scope field (list of safe output types to redact). + if scope, exists := configMap["scope"]; exists { + if scopeArr, ok := scope.([]any); ok { + cr.Scope = parseStringSliceAny(scopeArr, contentRedactionLog) + } + } + + // Parse runs-on field. + if runsOn, exists := configMap["runs-on"]; exists { + cr.RunsOn = renderRunsOnSnippet(runsOn) + } + + // Parse continue-on-error (bool or expression string). + if coe, exists := configMap["continue-on-error"]; exists { + switch v := coe.(type) { + case bool: + cr.ContinueOnError = &v + case string: + // Expression strings are not supported for this field; ignore. + contentRedactionLog.Printf("Content redaction: continue-on-error expression strings are not supported; ignoring %q", v) + } + } + + // Require at least one policy when not expression-controlled. + if cr.IfExpr == nil && len(cr.Policies) == 0 { + contentRedactionLog.Print("Content redaction: no policies provided; skipping") + return nil + } + + contentRedactionLog.Printf("Content redaction configured with %d policy entries, model=%q, on-failure=%q", + len(cr.Policies), cr.Model, cr.OnFailure) + return cr +} diff --git a/pkg/workflow/content_redaction_experimental_warning_test.go b/pkg/workflow/content_redaction_experimental_warning_test.go new file mode 100644 index 00000000000..004aa814c10 --- /dev/null +++ b/pkg/workflow/content_redaction_experimental_warning_test.go @@ -0,0 +1,126 @@ +//go:build integration + +package workflow + +import ( + "bytes" + "io" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/github/gh-aw/pkg/testutil" +) + +func TestContentRedactionExperimentalWarning(t *testing.T) { + tests := []struct { + name string + content string + expectWarning bool + }{ + { + name: "content-redaction enabled produces experimental warning", + content: `--- +on: workflow_dispatch +engine: copilot +safe-outputs: + add-comment: + content-redaction: + policies: "Do not disclose security vulnerabilities" +--- + +# Test Workflow +`, + expectWarning: true, + }, + { + name: "no content-redaction does not produce experimental warning", + content: `--- +on: workflow_dispatch +engine: copilot +safe-outputs: + add-comment: +--- + +# Test Workflow +`, + expectWarning: false, + }, + { + name: "content-redaction with inline string produces experimental warning", + content: `--- +on: workflow_dispatch +engine: copilot +safe-outputs: + add-comment: + content-redaction: "Never disclose internal credentials" +--- + +# Test Workflow +`, + expectWarning: true, + }, + { + name: "content-redaction with array produces experimental warning", + content: `--- +on: workflow_dispatch +engine: copilot +safe-outputs: + add-comment: + content-redaction: + - "https://corp.example.com/policy.md" + - "Never disclose CVE IDs before fix" +--- + +# Test Workflow +`, + expectWarning: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpDir := testutil.TempDir(t, "content-redaction-experimental-warning-test") + + testFile := filepath.Join(tmpDir, "test-workflow.md") + if err := os.WriteFile(testFile, []byte(tt.content), 0644); err != nil { + t.Fatal(err) + } + + oldStderr := os.Stderr + r, w, _ := os.Pipe() + os.Stderr = w + + compiler := NewCompiler() + compiler.SetStrictMode(false) + err := compiler.CompileWorkflow(testFile) + + w.Close() + os.Stderr = oldStderr + var buf bytes.Buffer + io.Copy(&buf, r) + stderrOutput := buf.String() + + if err != nil { + t.Errorf("expected compilation to succeed but it failed: %v", err) + return + } + + expectedMessage := "Using experimental feature: content-redaction" + if tt.expectWarning { + if !strings.Contains(stderrOutput, expectedMessage) { + t.Errorf("expected warning containing %q, got stderr:\n%s", expectedMessage, stderrOutput) + } + if compiler.GetWarningCount() == 0 { + t.Error("expected warning count > 0 but got 0") + } + return + } + + if strings.Contains(stderrOutput, expectedMessage) { + t.Errorf("did not expect warning %q, but got stderr:\n%s", expectedMessage, stderrOutput) + } + }) + } +} diff --git a/pkg/workflow/content_redaction_job.go b/pkg/workflow/content_redaction_job.go new file mode 100644 index 00000000000..f530ad6c445 --- /dev/null +++ b/pkg/workflow/content_redaction_job.go @@ -0,0 +1,309 @@ +// Package workflow - content_redaction job assembler. +package workflow + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/github/gh-aw/pkg/constants" + "github.com/github/gh-aw/pkg/logger" +) + +var contentRedactionJobLog = logger.New("workflow:content_redaction_job") + +// buildContentRedactionJob creates a separate content_redaction job that runs after the +// detection job (when threat detection is enabled) or after the agent job (when only content +// redaction is enabled). The job downloads the agent output, loads the configured policies, +// runs a fresh-context AI agent to review and rewrite text-bearing items, and outputs +// redaction_success and redaction_conclusion for downstream jobs. +// Returns nil if content redaction is not configured. +func (c *Compiler) buildContentRedactionJob(data *WorkflowData, detectionEnabled bool) (*Job, error) { + contentRedactionJobLog.Print("Building separate content_redaction job") + + if !IsContentRedactionEnabled(data.SafeOutputs) && !IsConditionalContentRedaction(data.SafeOutputs) { + contentRedactionJobLog.Print("Content redaction not configured, skipping content_redaction job") + return nil, nil + } + + cr := data.SafeOutputs.ContentRedaction + + var steps []string + + // Add setup action steps (same as other downstream jobs). + setupActionRef := c.resolveActionReference("./actions/setup", data) + if setupActionRef != "" || c.actionMode.IsScript() { + steps = append(steps, c.generateCheckoutActionsFolder(data)...) + redactionTraceID := fmt.Sprintf("${{ needs.%s.outputs.setup-trace-id }}", constants.ActivationJobName) + redactionParentSpanID := setupParentSpanNeedsExpr(constants.ActivationJobName) + steps = append(steps, c.generateSetupStep(data, setupActionRef, SetupActionDestination, false, redactionTraceID, redactionParentSpanID)...) + } + + // Download agent output artifact to access output files. + agentArtifactPrefix := artifactPrefixExprForAgentDownstreamJob(data) + steps = append(steps, buildAgentOutputDownloadSteps(agentArtifactPrefix, c.getActionPin)...) + + // Build policy-loading steps for each configured source. + steps = append(steps, buildLoadRedactionPoliciesStep(cr)...) + + // Build the main redaction step (github-script step). + steps = append(steps, c.buildContentRedactionEngineStep(cr)...) + + // Build the conclusion step that sets job outputs. + steps = append(steps, buildContentRedactionConclusionStep(cr)...) + + // Build job condition. + // The job should run after the agent completes (not skipped) and after detection (if enabled). + alwaysFunc := BuildFunctionCall("always") + agentNotSkipped := BuildNotEquals( + BuildPropertyAccess(fmt.Sprintf("needs.%s.result", constants.AgentJobName)), + BuildStringLiteral("skipped"), + ) + jobConditionNode := BuildAnd(alwaysFunc, agentNotSkipped) + + // Add detection success condition when detection is enabled. + if detectionEnabled { + jobConditionNode = BuildAnd(jobConditionNode, buildDetectionPassedCondition()) + } + + // When content redaction is expression-controlled, add the runtime expression. + if cr != nil && cr.IfExpr != nil { + rawExpr := extractRawExpression(*cr.IfExpr) + jobConditionNode = BuildAnd(jobConditionNode, &ExpressionNode{Expression: rawExpr}) + contentRedactionJobLog.Printf("Content redaction job condition includes runtime expression: %s", rawExpr) + } + + jobCondition := RenderCondition(jobConditionNode) + + // Build dependencies. + needs := []string{string(constants.AgentJobName), string(constants.ActivationJobName)} + if detectionEnabled { + needs = append(needs, string(constants.DetectionJobName)) + contentRedactionJobLog.Print("Added detection job dependency to content_redaction job") + } + + // Determine runs-on. + runsOn := "runs-on: ubuntu-latest" + if cr != nil && cr.RunsOn != "" { + runsOn = normalizeRunsOnSnippet(cr.RunsOn) + } + + // Determine permissions. + perms := NewPermissionsContentsRead() + if hasCopilotRequestsWritePermission(data) { + perms.Set(PermissionCopilotRequests, PermissionWrite) + } + permissions := perms.RenderToYAML() + + // Determine environment. + environment := data.Environment + + // Build job outputs. + outputs := map[string]string{ + "redaction_success": "${{ steps.redaction_conclusion.outputs.success }}", + "redaction_conclusion": "${{ steps.redaction_conclusion.outputs.conclusion }}", + } + + // Determine continue-on-error setting. + var continueOnError *bool + if cr != nil && cr.ContinueOnError != nil { + continueOnError = cr.ContinueOnError + if *continueOnError { + contentRedactionJobLog.Print("Content redaction job will continue on error (failures emit warnings)") + } + } + + job := &Job{ + Name: string(constants.ContentRedactionJobName), + Needs: needs, + If: jobCondition, + RunsOn: c.indentYAMLLines(runsOn, " "), + Environment: c.indentYAMLLines(environment, " "), + Permissions: permissions, + Steps: steps, + Outputs: outputs, + ContinueOnError: continueOnError, + } + + contentRedactionJobLog.Printf("Built content_redaction job with %d steps, depends on: %v", len(steps), needs) + return job, nil +} + +// buildLoadRedactionPoliciesStep creates steps that load the policy text from all configured +// sources into a concatenated policy file at /tmp/gh-aw/content-redaction/policy.md. +// Sources are processed in order: URLs are fetched via curl, repo-relative paths are read +// from the workspace checkout, and inline strings are written directly. +func buildLoadRedactionPoliciesStep(cr *ContentRedactionConfig) []string { + if cr == nil || len(cr.Policies) == 0 { + return []string{ + " - name: Load redaction policies (no-op)\n", + " id: load_policies\n", + " run: mkdir -p /tmp/gh-aw/content-redaction && echo '' > /tmp/gh-aw/content-redaction/policy.md\n", + } + } + + var sb strings.Builder + sb.WriteString(" - name: Load redaction policies\n") + sb.WriteString(" id: load_policies\n") + sb.WriteString(" env:\n") + sb.WriteString(" GH_TOKEN: ${{ github.token }}\n") + sb.WriteString(" run: |\n") + sb.WriteString(" set -euo pipefail\n") + sb.WriteString(" mkdir -p /tmp/gh-aw/content-redaction\n") + sb.WriteString(" POLICY_FILE=/tmp/gh-aw/content-redaction/policy.md\n") + sb.WriteString(" : > \"$POLICY_FILE\"\n") + + for i, source := range cr.Policies { + comment := source + if len(comment) > 60 { + comment = comment[:60] + "..." + } + fmt.Fprintf(&sb, " # Policy source %d: %s\n", i+1, comment) + if strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://") { + // URL: fetch at runtime via curl. + fmt.Fprintf(&sb, " if ! curl -fsSL %q >> \"$POLICY_FILE\"; then\n", source) + fmt.Fprintf(&sb, " echo '::error::Content redaction: failed to fetch policy from %s'\n", source) + fmt.Fprintf(&sb, " exit 1\n") + fmt.Fprintf(&sb, " fi\n") + } else if strings.HasPrefix(source, "./") || strings.HasPrefix(source, ".github/") || strings.HasPrefix(source, "/") { + // Repo-relative or absolute path. + fmt.Fprintf(&sb, " if [ ! -f %q ]; then\n", source) + fmt.Fprintf(&sb, " echo '::error::Content redaction: policy file not found: %s'\n", source) + fmt.Fprintf(&sb, " exit 1\n") + fmt.Fprintf(&sb, " fi\n") + fmt.Fprintf(&sb, " cat %q >> \"$POLICY_FILE\"\n", source) + } else { + // Inline text literal: write directly. + escaped := strings.ReplaceAll(source, "'", "'\\''") + fmt.Fprintf(&sb, " printf '%%s\\n' '%s' >> \"$POLICY_FILE\"\n", escaped) + } + sb.WriteString(" echo '' >> \"$POLICY_FILE\"\n") + } + + sb.WriteString(" # Verify policy file is not empty\n") + sb.WriteString(" if [ ! -s \"$POLICY_FILE\" ]; then\n") + sb.WriteString(" echo '::error::Content redaction: all policy sources failed or were empty'\n") + sb.WriteString(" exit 1\n") + sb.WriteString(" fi\n") + sb.WriteString(" echo \"Policy loaded ($(wc -c < \"$POLICY_FILE\") bytes)\"\n") + + return []string{sb.String()} +} + +// buildContentRedactionEngineStep creates the main redaction step that executes +// the content_redaction.cjs script with AWF. It reads the agent output items, +// applies the loaded policy, and writes the redacted output back for safe_outputs to consume. +func (c *Compiler) buildContentRedactionEngineStep(cr *ContentRedactionConfig) []string { + // Determine the model to use (with fallback to a cost-effective default). + model := "gpt-4o-mini" + if cr != nil && cr.Model != "" { + model = cr.Model + } + + onFailure := ContentRedactionOnFailureBlock + if cr != nil && cr.OnFailure == ContentRedactionOnFailureWarn { + onFailure = ContentRedactionOnFailureWarn + } + + // Build the scope list as a JSON string literal for env var. + scopeJSONString := "[]" + if cr != nil && len(cr.Scope) > 0 { + quoted := make([]string, len(cr.Scope)) + for i, s := range cr.Scope { + quoted[i] = fmt.Sprintf("\"%s\"", s) + } + scopeJSONString = "[" + strings.Join(quoted, ", ") + "]" + } + + var steps []string + steps = append(steps, " - name: Run content redaction\n") + steps = append(steps, " id: run_redaction\n") + steps = append(steps, " if: always() && steps.load_policies.outcome == 'success'\n") + steps = append(steps, " env:\n") + steps = append(steps, " GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}\n") + steps = append(steps, " POLICY_FILE: /tmp/gh-aw/content-redaction/policy.md\n") + steps = append(steps, " REDACTION_MODEL: "+model+"\n") + steps = append(steps, " ON_FAILURE: "+onFailure+"\n") + steps = append(steps, " SCOPE: '"+scopeJSONString+"'\n") + steps = append(steps, " run: |\n") + steps = append(steps, " set -e\n") + steps = append(steps, " # Resolve node executable\n") + steps = append(steps, " GH_AW_NODE_EXEC=\"${GH_AW_NODE_BIN:-}\"\n") + steps = append(steps, " if [ -z \"$GH_AW_NODE_EXEC\" ] || [ ! -x \"$GH_AW_NODE_EXEC\" ]; then\n") + steps = append(steps, " GH_AW_NODE_EXEC=\"$(command -v node 2>/dev/null || true)\"\n") + steps = append(steps, " fi\n") + steps = append(steps, " if [ -z \"$GH_AW_NODE_EXEC\" ]; then\n") + steps = append(steps, " echo \"::error::node runtime missing on this runner — check runtimes.node in workflow YAML\"\n") + steps = append(steps, " exit 127\n") + steps = append(steps, " fi\n") + steps = append(steps, " # Configure AWF for content redaction\n") + steps = append(steps, " AWF_CONFIG_FILE=\"${RUNNER_TEMP}/gh-aw/content-redaction-awf-config.json\"\n") + steps = append(steps, c.buildContentRedactionAWFConfigSetup()) + steps = append(steps, " # Execute content redaction script via AWF\n") + steps = append(steps, " awf --config \"${AWF_CONFIG_FILE}\" \\\n") + steps = append(steps, " --container-workdir \"${GITHUB_WORKSPACE}\" \\\n") + steps = append(steps, " --mount \"${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:rw\" \\\n") + steps = append(steps, " --mount \"/tmp/gh-aw:/tmp/gh-aw:rw\" \\\n") + steps = append(steps, " --env-all \\\n") + steps = append(steps, " --log-level info \\\n") + steps = append(steps, " --skip-pull \\\n") + steps = append(steps, " -- bash -c \"\\\"\\$GH_AW_NODE_EXEC\\\" \\\"\\${RUNNER_TEMP}/gh-aw/actions/content_redaction.cjs\\\"\"\n") + + return steps +} + +// buildContentRedactionAWFConfigSetup generates the shell script to create AWF config JSON +// for content redaction. This config enables minimal network access needed for AI model API calls. +func (c *Compiler) buildContentRedactionAWFConfigSetup() string { + // Build minimal AWF config for content redaction: + // - Enable API proxy for model access + // - Allow minimal required domains + // - Use standard firewall version + config := map[string]any{ + "apiProxy": map[string]any{ + "enabled": true, + }, + } + + // Marshal as compact JSON (single line) to avoid YAML parsing issues + configJSON, err := json.Marshal(config) + if err != nil { + contentRedactionJobLog.Printf("Warning: failed to marshal AWF config for content redaction: %v", err) + // Fallback to minimal valid config + configJSON = []byte(`{"apiProxy":{"enabled":true}}`) + } + + // Escape the JSON for shell (preserve $ for env vars) + escaped := shellEscapeArg(string(configJSON)) + return fmt.Sprintf(" printf '%%s\\n' %s > \"${AWF_CONFIG_FILE}\"\n", escaped) +} + +// buildContentRedactionConclusionStep creates the step that sets the job conclusion outputs. +func buildContentRedactionConclusionStep(cr *ContentRedactionConfig) []string { + onFailure := ContentRedactionOnFailureBlock + if cr != nil && cr.OnFailure == ContentRedactionOnFailureWarn { + onFailure = ContentRedactionOnFailureWarn + } + + return []string{ + " - name: Set redaction conclusion\n", + " id: redaction_conclusion\n", + " if: always()\n", + " env:\n", + " HAS_BLOCKED: ${{ steps.run_redaction.outputs.has_blocked_items }}\n", + " SKIPPED: ${{ steps.run_redaction.outputs.skipped }}\n", + " run: |\n", + " if [[ \"$SKIPPED\" == \"true\" ]]; then\n", + " echo \"success=true\" >> \"$GITHUB_OUTPUT\"\n", + " echo \"conclusion=skipped\" >> \"$GITHUB_OUTPUT\"\n", + fmt.Sprintf(" elif [[ \"$HAS_BLOCKED\" == \"true\" && \"%s\" == \"%s\" ]]; then\n", onFailure, ContentRedactionOnFailureBlock), + " echo \"success=false\" >> \"$GITHUB_OUTPUT\"\n", + " echo \"conclusion=blocked\" >> \"$GITHUB_OUTPUT\"\n", + " echo \"::error::Content redaction blocked items that could not be made policy-compliant\"\n", + " exit 1\n", + " else\n", + " echo \"success=true\" >> \"$GITHUB_OUTPUT\"\n", + " echo \"conclusion=passed\" >> \"$GITHUB_OUTPUT\"\n", + " fi\n", + } +} diff --git a/pkg/workflow/content_redaction_test.go b/pkg/workflow/content_redaction_test.go new file mode 100644 index 00000000000..efcf2f309bd --- /dev/null +++ b/pkg/workflow/content_redaction_test.go @@ -0,0 +1,695 @@ +//go:build !integration + +package workflow + +import ( + "os" + "path/filepath" + "slices" + "strings" + "testing" + + "github.com/github/gh-aw/pkg/constants" + "github.com/github/gh-aw/pkg/stringutil" + "github.com/github/gh-aw/pkg/testutil" +) + +// TestParseContentRedactionConfig validates parsing of the content-redaction configuration +// from the safe-outputs frontmatter. +func TestParseContentRedactionConfig(t *testing.T) { + compiler := NewCompiler() + + tests := []struct { + name string + outputMap map[string]any + expectedNil bool + expectedPolicies []string + expectedModel string + expectedOnFail string + expectedScope []string + expectExprExpr bool + }{ + { + name: "missing content-redaction should return nil", + outputMap: map[string]any{}, + expectedNil: true, + }, + { + name: "boolean false should return nil", + outputMap: map[string]any{ + "content-redaction": false, + }, + expectedNil: true, + }, + { + name: "boolean true (no policies) should return nil", + outputMap: map[string]any{ + "content-redaction": true, + }, + expectedNil: true, + }, + { + name: "single inline string", + outputMap: map[string]any{ + "content-redaction": "Do not disclose security vulnerabilities", + }, + expectedNil: false, + expectedPolicies: []string{"Do not disclose security vulnerabilities"}, + }, + { + name: "array shorthand", + outputMap: map[string]any{ + "content-redaction": []any{ + "https://corp.example.com/policy.md", + ".github/policies/inclusive-language.md", + "Never mention internal codenames", + }, + }, + expectedNil: false, + expectedPolicies: []string{ + "https://corp.example.com/policy.md", + ".github/policies/inclusive-language.md", + "Never mention internal codenames", + }, + }, + { + name: "array shorthand with empty array returns nil", + outputMap: map[string]any{ + "content-redaction": []any{}, + }, + expectedNil: true, + }, + { + name: "object form with all fields", + outputMap: map[string]any{ + "content-redaction": map[string]any{ + "policies": "https://corp.example.com/policy.md", + "model": "gpt-4o-mini", + "on-failure": "warn", + "scope": []any{"add-comment", "create-issue"}, + "continue-on-error": true, + }, + }, + expectedNil: false, + expectedPolicies: []string{"https://corp.example.com/policy.md"}, + expectedModel: "gpt-4o-mini", + expectedOnFail: "warn", + expectedScope: []string{"add-comment", "create-issue"}, + }, + { + name: "object form with policies array", + outputMap: map[string]any{ + "content-redaction": map[string]any{ + "policies": []any{ + "https://corp.example.com/policy.md", + ".github/policies/redaction.md", + }, + }, + }, + expectedNil: false, + expectedPolicies: []string{ + "https://corp.example.com/policy.md", + ".github/policies/redaction.md", + }, + }, + { + name: "object form with if: false returns nil", + outputMap: map[string]any{ + "content-redaction": map[string]any{ + "if": false, + "policies": "Some policy", + }, + }, + expectedNil: true, + }, + { + name: "object form without policies returns nil", + outputMap: map[string]any{ + "content-redaction": map[string]any{ + "model": "gpt-4o-mini", + }, + }, + expectedNil: true, + }, + { + name: "runtime expression string enables conditional redaction", + outputMap: map[string]any{ + "content-redaction": "${{ inputs.enable-content-redaction }}", + }, + expectedNil: false, + expectExprExpr: true, + }, + { + name: "object form with expression-controlled if", + outputMap: map[string]any{ + "content-redaction": map[string]any{ + "if": "${{ inputs.enable-redaction }}", + "policies": "Do not disclose CVE IDs", + }, + }, + expectedNil: false, + expectExprExpr: true, + expectedPolicies: []string{"Do not disclose CVE IDs"}, + }, + { + name: "unknown on-failure value is ignored (defaults to block)", + outputMap: map[string]any{ + "content-redaction": map[string]any{ + "policies": "Policy", + "on-failure": "invalid-value", + }, + }, + expectedNil: false, + expectedPolicies: []string{"Policy"}, + expectedOnFail: "", // unknown value, field is not set + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cr := compiler.parseContentRedactionConfig(tt.outputMap) + + if tt.expectedNil { + if cr != nil { + t.Errorf("expected nil ContentRedactionConfig, got %+v", cr) + } + return + } + + if cr == nil { + t.Fatal("expected non-nil ContentRedactionConfig, got nil") + } + + if tt.expectExprExpr { + if cr.IfExpr == nil { + t.Error("expected IfExpr to be set") + } + } + + if len(tt.expectedPolicies) > 0 { + if len(cr.Policies) != len(tt.expectedPolicies) { + t.Errorf("expected %d policy entries, got %d: %v", len(tt.expectedPolicies), len(cr.Policies), cr.Policies) + } else { + for i, want := range tt.expectedPolicies { + if cr.Policies[i] != want { + t.Errorf("policy[%d]: expected %q, got %q", i, want, cr.Policies[i]) + } + } + } + } + + if tt.expectedModel != "" && cr.Model != tt.expectedModel { + t.Errorf("model: expected %q, got %q", tt.expectedModel, cr.Model) + } + + if tt.expectedOnFail != "" && cr.OnFailure != tt.expectedOnFail { + t.Errorf("on-failure: expected %q, got %q", tt.expectedOnFail, cr.OnFailure) + } + + if len(tt.expectedScope) > 0 { + if len(cr.Scope) != len(tt.expectedScope) { + t.Errorf("scope: expected %v, got %v", tt.expectedScope, cr.Scope) + } + } + }) + } +} + +// TestIsContentRedactionEnabled validates the IsContentRedactionEnabled helper. +func TestIsContentRedactionEnabled(t *testing.T) { + tests := []struct { + name string + so *SafeOutputsConfig + expected bool + }{ + { + name: "nil safe outputs", + so: nil, + expected: false, + }, + { + name: "no content redaction configured", + so: &SafeOutputsConfig{}, + expected: false, + }, + { + name: "content redaction with agent", + so: &SafeOutputsConfig{ + ContentRedaction: &ContentRedactionConfig{ + Policies: []string{"policy text"}, + }, + }, + expected: true, + }, + { + name: "content redaction without policies (not enabled)", + so: &SafeOutputsConfig{ + ContentRedaction: &ContentRedactionConfig{}, + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := IsContentRedactionEnabled(tt.so) + if got != tt.expected { + t.Errorf("IsContentRedactionEnabled() = %v, want %v", got, tt.expected) + } + }) + } +} + +// TestContentRedactionJobOutputs verifies that the content_redaction job exposes +// the correct outputs (redaction_success, redaction_conclusion). +func TestContentRedactionJobOutputs(t *testing.T) { + compiler := NewCompiler() + + data := &WorkflowData{ + Name: "test-workflow", + AI: "copilot", + SafeOutputs: &SafeOutputsConfig{ + ThreatDetection: &ThreatDetectionConfig{}, + AddComments: &AddCommentsConfig{}, + ContentRedaction: &ContentRedactionConfig{ + Policies: []string{"Do not disclose security vulnerabilities"}, + }, + }, + } + + job, err := compiler.buildContentRedactionJob(data, true) + if err != nil { + t.Fatalf("buildContentRedactionJob() error = %v", err) + } + if job == nil { + t.Fatal("expected non-nil content_redaction job, got nil") + } + + if _, ok := job.Outputs["redaction_success"]; !ok { + t.Error("content_redaction job missing redaction_success output") + } + if _, ok := job.Outputs["redaction_conclusion"]; !ok { + t.Error("content_redaction job missing redaction_conclusion output") + } + + if job.Name != string(constants.ContentRedactionJobName) { + t.Errorf("job name = %q, want %q", job.Name, string(constants.ContentRedactionJobName)) + } +} + +// TestContentRedactionJobDependsOnDetection verifies that the content_redaction job +// depends on the detection job when threat detection is enabled. +func TestContentRedactionJobDependsOnDetection(t *testing.T) { + compiler := NewCompiler() + + data := &WorkflowData{ + Name: "test-workflow", + AI: "copilot", + SafeOutputs: &SafeOutputsConfig{ + ThreatDetection: &ThreatDetectionConfig{}, + AddComments: &AddCommentsConfig{}, + ContentRedaction: &ContentRedactionConfig{ + Policies: []string{"Policy"}, + }, + }, + } + + // With detection enabled. + job, err := compiler.buildContentRedactionJob(data, true) + if err != nil { + t.Fatalf("buildContentRedactionJob() error = %v", err) + } + + found := slices.Contains(job.Needs, string(constants.DetectionJobName)) + if !found { + t.Errorf("content_redaction job needs %v, expected %q to be present", job.Needs, string(constants.DetectionJobName)) + } + + // Without detection enabled. + jobNoDetection, err := compiler.buildContentRedactionJob(data, false) + if err != nil { + t.Fatalf("buildContentRedactionJob() error = %v", err) + } + + if slices.Contains(jobNoDetection.Needs, string(constants.DetectionJobName)) { + t.Errorf("content_redaction job should not depend on detection when detection is disabled, needs: %v", jobNoDetection.Needs) + } +} + +// TestContentRedactionJobCondition verifies that the content_redaction job condition +// always includes always() so it runs even after other job failures. +func TestContentRedactionJobCondition(t *testing.T) { + compiler := NewCompiler() + + data := &WorkflowData{ + Name: "test-workflow", + AI: "copilot", + SafeOutputs: &SafeOutputsConfig{ + ThreatDetection: &ThreatDetectionConfig{}, + AddComments: &AddCommentsConfig{}, + ContentRedaction: &ContentRedactionConfig{ + Policies: []string{"Policy"}, + }, + }, + } + + job, err := compiler.buildContentRedactionJob(data, true) + if err != nil { + t.Fatalf("buildContentRedactionJob() error = %v", err) + } + + if !strings.Contains(job.If, "always()") { + t.Errorf("content_redaction job condition should include always(), got: %q", job.If) + } + if !strings.Contains(job.If, string(constants.AgentJobName)) { + t.Errorf("content_redaction job condition should reference agent job, got: %q", job.If) + } +} + +// TestContentRedactionLoadPoliciesStep validates the policy loading step for various source types. +func TestContentRedactionLoadPoliciesStep(t *testing.T) { + tests := []struct { + name string + cr *ContentRedactionConfig + wantCurl bool + wantFileOp bool + wantPrintf bool + }{ + { + name: "URL policy uses curl", + cr: &ContentRedactionConfig{ + Policies: []string{"https://corp.example.com/policy.md"}, + }, + wantCurl: true, + }, + { + name: "repo-relative path uses file check", + cr: &ContentRedactionConfig{ + Policies: []string{".github/policies/redaction.md"}, + }, + wantFileOp: true, + }, + { + name: "inline policy uses printf", + cr: &ContentRedactionConfig{ + Policies: []string{"Never disclose internal codenames"}, + }, + wantPrintf: true, + }, + { + name: "./relative path uses file check", + cr: &ContentRedactionConfig{ + Policies: []string{"./policies/redaction.md"}, + }, + wantFileOp: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + steps := buildLoadRedactionPoliciesStep(tt.cr) + combined := strings.Join(steps, "") + + if tt.wantCurl && !strings.Contains(combined, "curl") { + t.Errorf("expected curl for URL policy; got:\n%s", combined) + } + if tt.wantFileOp && !strings.Contains(combined, "if [ ! -f") { + t.Errorf("expected file existence check for path policy; got:\n%s", combined) + } + if tt.wantPrintf && !strings.Contains(combined, "printf") { + t.Errorf("expected printf for inline policy; got:\n%s", combined) + } + }) + } +} + +// TestContentRedactionNilConfigReturnsNilJob verifies that no job is created when +// content redaction is not configured. +func TestContentRedactionNilConfigReturnsNilJob(t *testing.T) { + compiler := NewCompiler() + + data := &WorkflowData{ + Name: "test-workflow", + AI: "copilot", + SafeOutputs: &SafeOutputsConfig{ + ThreatDetection: &ThreatDetectionConfig{}, + AddComments: &AddCommentsConfig{}, + }, + } + + job, err := compiler.buildContentRedactionJob(data, true) + if err != nil { + t.Fatalf("buildContentRedactionJob() error = %v", err) + } + if job != nil { + t.Errorf("expected nil job when content redaction not configured, got: %+v", job) + } +} + +// TestContentRedactionCompiledWorkflowHasJob verifies that a compiled workflow with +// content-redaction configured includes the content_redaction job in the generated YAML. +func TestContentRedactionCompiledWorkflowHasJob(t *testing.T) { + tmpDir := testutil.TempDir(t, "content-redaction-test-*") + workflowPath := filepath.Join(tmpDir, "test-workflow.md") + + frontmatter := `--- +on: workflow_dispatch +permissions: + contents: read +engine: copilot +safe-outputs: + add-comment: + content-redaction: + policies: "Do not disclose security vulnerabilities in public comments" +--- + +# Test Workflow + +Test workflow with content redaction. +` + + if err := os.WriteFile(workflowPath, []byte(frontmatter), 0644); err != nil { + t.Fatalf("Failed to write workflow file: %v", err) + } + + compiler := NewCompiler() + if err := compiler.CompileWorkflow(workflowPath); err != nil { + t.Fatalf("CompileWorkflow() error = %v", err) + } + + lockPath := stringutil.MarkdownToLockFile(workflowPath) + yamlBytes, err := os.ReadFile(lockPath) + if err != nil { + t.Fatalf("Failed to read compiled YAML: %v", err) + } + yaml := string(yamlBytes) + + // The content_redaction job must be present. + contentRedactionSection := extractJobSection(yaml, string(constants.ContentRedactionJobName)) + if contentRedactionSection == "" { + t.Fatalf("content_redaction job not found in compiled YAML:\n%s", yaml[:min(len(yaml), 3000)]) + } + + // The job must expose redaction_success and redaction_conclusion outputs. + if !strings.Contains(contentRedactionSection, "redaction_success:") { + t.Errorf("content_redaction job missing redaction_success output; section:\n%s", contentRedactionSection) + } + if !strings.Contains(contentRedactionSection, "redaction_conclusion:") { + t.Errorf("content_redaction job missing redaction_conclusion output; section:\n%s", contentRedactionSection) + } +} + +// TestContentRedactionSafeOutputsDependsOnJob verifies that the safe_outputs job depends on +// content_redaction when content redaction is configured. +func TestContentRedactionSafeOutputsDependsOnJob(t *testing.T) { + tmpDir := testutil.TempDir(t, "content-redaction-test-*") + workflowPath := filepath.Join(tmpDir, "test-workflow.md") + + frontmatter := `--- +on: workflow_dispatch +permissions: + contents: read +engine: copilot +safe-outputs: + add-comment: + content-redaction: + policies: "Policy text" +--- + +# Test + +Test. +` + + if err := os.WriteFile(workflowPath, []byte(frontmatter), 0644); err != nil { + t.Fatalf("Failed to write workflow file: %v", err) + } + + compiler := NewCompiler() + if err := compiler.CompileWorkflow(workflowPath); err != nil { + t.Fatalf("CompileWorkflow() error = %v", err) + } + + lockPath := stringutil.MarkdownToLockFile(workflowPath) + yamlBytes, err := os.ReadFile(lockPath) + if err != nil { + t.Fatalf("Failed to read compiled YAML: %v", err) + } + yaml := string(yamlBytes) + + // The safe_outputs job must depend on content_redaction. + safeOutputsSection := extractJobSection(yaml, "safe_outputs") + if safeOutputsSection == "" { + t.Fatal("safe_outputs job not found in compiled YAML") + } + if !strings.Contains(safeOutputsSection, string(constants.ContentRedactionJobName)) { + t.Errorf("safe_outputs job should depend on content_redaction; needs section:\n%s", safeOutputsSection[:min(len(safeOutputsSection), 500)]) + } +} + +// TestContentRedactionArrayShorthandCompiles verifies that the array shorthand form compiles. +func TestContentRedactionArrayShorthandCompiles(t *testing.T) { + tmpDir := testutil.TempDir(t, "content-redaction-test-*") + workflowPath := filepath.Join(tmpDir, "test-workflow.md") + + frontmatter := `--- +on: workflow_dispatch +permissions: + contents: read +engine: copilot +safe-outputs: + add-comment: + content-redaction: + - "https://corp.example.com/content-policy.md" + - "Never mention internal project codenames" +--- + +# Test + +Test. +` + + if err := os.WriteFile(workflowPath, []byte(frontmatter), 0644); err != nil { + t.Fatalf("Failed to write workflow file: %v", err) + } + + compiler := NewCompiler() + if err := compiler.CompileWorkflow(workflowPath); err != nil { + t.Fatalf("CompileWorkflow() with array shorthand error = %v", err) + } + + lockPath := stringutil.MarkdownToLockFile(workflowPath) + if _, err := os.Stat(lockPath); err != nil { + t.Fatalf("Lock file not created: %v", err) + } +} + +// TestContentRedactionContinueOnError verifies that the continue-on-error field +// is properly propagated from config to the compiled job YAML. +func TestContentRedactionContinueOnError(t *testing.T) { + tmpDir := testutil.TempDir(t, "content-redaction-test-*") + workflowPath := filepath.Join(tmpDir, "test-workflow.md") + + frontmatter := `--- +on: workflow_dispatch +permissions: + contents: read +engine: copilot +safe-outputs: + add-comment: + content-redaction: + policies: "Do not disclose security vulnerabilities" + continue-on-error: true +--- + +# Test Workflow + +Test workflow with continue-on-error. +` + + if err := os.WriteFile(workflowPath, []byte(frontmatter), 0644); err != nil { + t.Fatalf("Failed to write workflow file: %v", err) + } + + compiler := NewCompiler() + if err := compiler.CompileWorkflow(workflowPath); err != nil { + t.Fatalf("CompileWorkflow() error = %v", err) + } + + lockPath := stringutil.MarkdownToLockFile(workflowPath) + yamlBytes, err := os.ReadFile(lockPath) + if err != nil { + t.Fatalf("Failed to read compiled YAML: %v", err) + } + yaml := string(yamlBytes) + + // The content_redaction job must have continue-on-error: true. + contentRedactionSection := extractJobSection(yaml, string(constants.ContentRedactionJobName)) + if contentRedactionSection == "" { + t.Fatalf("content_redaction job not found in compiled YAML") + } + + if !strings.Contains(contentRedactionSection, "continue-on-error: true") { + t.Errorf("content_redaction job missing 'continue-on-error: true'; section:\n%s", contentRedactionSection) + } +} + +// TestContentRedactionPolicyLoadFailureExits verifies that policy loading failures +// cause the job to exit with an error code (not just a warning). +func TestContentRedactionPolicyLoadFailureExits(t *testing.T) { + tmpDir := testutil.TempDir(t, "content-redaction-test-*") + workflowPath := filepath.Join(tmpDir, "test-workflow.md") + + frontmatter := `--- +on: workflow_dispatch +permissions: + contents: read +engine: copilot +safe-outputs: + add-comment: + content-redaction: + policies: ".github/missing-policy.md" +--- + +# Test Workflow + +Test workflow with missing policy file. +` + + if err := os.WriteFile(workflowPath, []byte(frontmatter), 0644); err != nil { + t.Fatalf("Failed to write workflow file: %v", err) + } + + compiler := NewCompiler() + if err := compiler.CompileWorkflow(workflowPath); err != nil { + t.Fatalf("CompileWorkflow() error = %v", err) + } + + lockPath := stringutil.MarkdownToLockFile(workflowPath) + yamlBytes, err := os.ReadFile(lockPath) + if err != nil { + t.Fatalf("Failed to read compiled YAML: %v", err) + } + yaml := string(yamlBytes) + + // The load policies step must check for file existence and exit 1 on failure. + contentRedactionSection := extractJobSection(yaml, string(constants.ContentRedactionJobName)) + if contentRedactionSection == "" { + t.Fatalf("content_redaction job not found in compiled YAML") + } + + // Verify the step checks for file existence. + if !strings.Contains(contentRedactionSection, "if [ ! -f") { + t.Errorf("Policy loading step should check file existence; section:\n%s", contentRedactionSection) + } + + // Verify the step exits with error code 1 on failure. + if !strings.Contains(contentRedactionSection, "exit 1") { + t.Errorf("Policy loading step should exit 1 on failure; section:\n%s", contentRedactionSection) + } + + // Verify the step checks for empty policy file. + if !strings.Contains(contentRedactionSection, "if [ ! -s") { + t.Errorf("Policy loading step should check for empty policy file; section:\n%s", contentRedactionSection) + } +} diff --git a/pkg/workflow/data/action_pins.json b/pkg/workflow/data/action_pins.json index cea16445e6a..c675b847054 100644 --- a/pkg/workflow/data/action_pins.json +++ b/pkg/workflow/data/action_pins.json @@ -1,13 +1,11 @@ { "entries": { "actions-ecosystem/action-add-labels@v1.1.3": { - "repo": "actions-ecosystem/action-add-labels", - "version": "v1.1.3", - "sha": "c96b68fec76a0987cd93957189e9abd0b9a72ff1", + "action_description": "Add labels to an issue or a pull request.", "inputs": { "github_token": { - "description": "A GitHub token.", - "default": "${{ github.token }}" + "default": "${{ github.token }}", + "description": "A GitHub token." }, "labels": { "description": "The labels' name to be added. Must be separated with line breaks if there're multiple labels.", @@ -17,269 +15,271 @@ "description": "The number of the issue or pull request." }, "repo": { - "description": "The owner and repository name. e.g.) Codertocat/Hello-World", - "default": "${{ github.repository }}" + "default": "${{ github.repository }}", + "description": "The owner and repository name. e.g.) Codertocat/Hello-World" } }, - "action_description": "Add labels to an issue or a pull request." + "repo": "actions-ecosystem/action-add-labels", + "sha": "c96b68fec76a0987cd93957189e9abd0b9a72ff1", + "version": "v1.1.3" }, "actions/cache/restore@v6.1.0": { "repo": "actions/cache/restore", - "version": "v6.1.0", - "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9" + "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9", + "version": "v6.1.0" }, "actions/cache/save@v6.1.0": { "repo": "actions/cache/save", - "version": "v6.1.0", - "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9" + "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9", + "version": "v6.1.0" }, "actions/cache@v6.1.0": { "repo": "actions/cache", - "version": "v6.1.0", - "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9" + "sha": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9", + "version": "v6.1.0" }, "actions/checkout@v7.0.0": { "repo": "actions/checkout", - "version": "v7.0.0", - "sha": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" + "sha": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "version": "v7.0.0" }, "actions/create-github-app-token@v3.2.0": { "repo": "actions/create-github-app-token", - "version": "v3.2.0", - "sha": "bcd2ba49218906704ab6c1aa796996da409d3eb1" + "sha": "bcd2ba49218906704ab6c1aa796996da409d3eb1", + "version": "v3.2.0" }, "actions/download-artifact@v8.0.1": { "repo": "actions/download-artifact", - "version": "v8.0.1", - "sha": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c" + "sha": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", + "version": "v8.0.1" }, "actions/github-script@v9.0.0": { "repo": "actions/github-script", - "version": "v9.0.0", - "sha": "3a2844b7e9c422d3c10d287c895573f7108da1b3" + "sha": "3a2844b7e9c422d3c10d287c895573f7108da1b3", + "version": "v9.0.0" }, "actions/setup-dotnet@v5.4.0": { "repo": "actions/setup-dotnet", - "version": "v5.4.0", - "sha": "26b0ec14cb23fa6904739307f278c14f94c95bf1" + "sha": "26b0ec14cb23fa6904739307f278c14f94c95bf1", + "version": "v5.4.0" }, "actions/setup-go@v6.5.0": { "repo": "actions/setup-go", - "version": "v6.5.0", - "sha": "924ae3a1cded613372ab5595356fb5720e22ba16" + "sha": "924ae3a1cded613372ab5595356fb5720e22ba16", + "version": "v6.5.0" }, "actions/setup-java@v5.4.0": { "repo": "actions/setup-java", - "version": "v5.4.0", - "sha": "1bcf9fb12cf4aa7d266a90ae39939e61372fe520" + "sha": "1bcf9fb12cf4aa7d266a90ae39939e61372fe520", + "version": "v5.4.0" }, "actions/setup-node@v6.4.0": { "repo": "actions/setup-node", - "version": "v6.4.0", - "sha": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e" + "sha": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e", + "version": "v6.4.0" }, "actions/setup-python@v6.3.0": { "repo": "actions/setup-python", - "version": "v6.3.0", - "sha": "ece7cb06caefa5fff74198d8649806c4678c61a1" + "sha": "ece7cb06caefa5fff74198d8649806c4678c61a1", + "version": "v6.3.0" }, "actions/upload-artifact@v7.0.1": { "repo": "actions/upload-artifact", - "version": "v7.0.1", - "sha": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" + "sha": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "version": "v7.0.1" }, "anchore/sbom-action@v0.24.0": { "repo": "anchore/sbom-action", - "version": "v0.24.0", - "sha": "e22c389904149dbc22b58101806040fa8d37a610" + "sha": "e22c389904149dbc22b58101806040fa8d37a610", + "version": "v0.24.0" }, "astral-sh/setup-uv@v8.2.0": { "repo": "astral-sh/setup-uv", - "version": "v8.2.0", - "sha": "fac544c07dec837d0ccb6301d7b5580bf5edae39" + "sha": "fac544c07dec837d0ccb6301d7b5580bf5edae39", + "version": "v8.2.0" }, "denoland/setup-deno@v2.0.4": { "repo": "denoland/setup-deno", - "version": "v2.0.4", - "sha": "667a34cdef165d8d2b2e98dde39547c9daac7282" + "sha": "667a34cdef165d8d2b2e98dde39547c9daac7282", + "version": "v2.0.4" }, "docker/build-push-action@v7.2.0": { + "released_at": "2026-05-21T15:23:58Z", "repo": "docker/build-push-action", - "version": "v7.2.0", "sha": "f9f3042f7e2789586610d6e8b85c8f03e5195baf", - "released_at": "2026-05-21T15:23:58Z" + "version": "v7.2.0" }, "docker/login-action@v4.2.0": { + "released_at": "2026-05-22T11:55:00Z", "repo": "docker/login-action", - "version": "v4.2.0", "sha": "650006c6eb7dba73a995cc03b0b2d7f5ca915bee", - "released_at": "2026-05-22T11:55:00Z" + "version": "v4.2.0" }, "docker/metadata-action@v6.1.0": { "repo": "docker/metadata-action", - "version": "v6.1.0", - "sha": "80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9" + "sha": "80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9", + "version": "v6.1.0" }, "docker/setup-buildx-action@v4.1.0": { "repo": "docker/setup-buildx-action", - "version": "v4.1.0", - "sha": "d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5" + "sha": "d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5", + "version": "v4.1.0" }, "erlef/setup-beam@v1.24.0": { "repo": "erlef/setup-beam", - "version": "v1.24.0", - "sha": "fc68ffb90438ef2936bbb3251622353b3dcb2f93" + "sha": "fc68ffb90438ef2936bbb3251622353b3dcb2f93", + "version": "v1.24.0" }, "github/codeql-action/upload-sarif@v4.36.2": { "repo": "github/codeql-action/upload-sarif", - "version": "v4.36.2", - "sha": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e" + "sha": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "version": "v4.36.2" }, "github/stale-repos@v9.0.15": { "repo": "github/stale-repos", - "version": "v9.0.15", - "sha": "a0db8bfe2318ed6ddbd01e95d7633803f99f77e9" + "sha": "a0db8bfe2318ed6ddbd01e95d7633803f99f77e9", + "version": "v9.0.15" }, "haskell-actions/setup@v2.11.0": { "repo": "haskell-actions/setup", - "version": "v2.11.0", - "sha": "cd0d9bdd65b20557f41bea4dbe43d0b5fbbfe553" + "sha": "cd0d9bdd65b20557f41bea4dbe43d0b5fbbfe553", + "version": "v2.11.0" }, "oven-sh/setup-bun@v2.2.0": { "repo": "oven-sh/setup-bun", - "version": "v2.2.0", - "sha": "0c5077e51419868618aeaa5fe8019c62421857d6" + "sha": "0c5077e51419868618aeaa5fe8019c62421857d6", + "version": "v2.2.0" }, "ruby/setup-ruby@v1.314.0": { "repo": "ruby/setup-ruby", - "version": "v1.314.0", - "sha": "9eb537ca036ebaed86729dcb9309076e4c5c3b74" + "sha": "9eb537ca036ebaed86729dcb9309076e4c5c3b74", + "version": "v1.314.0" }, "safedep/pmg@v1": { "repo": "safedep/pmg", - "version": "v1", - "sha": "46cc70db535107183c9e752bb55d1d5c5f1a9290" + "sha": "46cc70db535107183c9e752bb55d1d5c5f1a9290", + "version": "v1" }, "super-linter/super-linter@v8.7.0": { "repo": "super-linter/super-linter", - "version": "v8.7.0", - "sha": "4ce20838b8ab83717e78138c5b3a1407148e0918" + "sha": "4ce20838b8ab83717e78138c5b3a1407148e0918", + "version": "v8.7.0" } }, "containers": { "alpine:latest": { - "image": "alpine:latest", "digest": "sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11", + "image": "alpine:latest", "pinned_image": "alpine:latest@sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11" }, "docker.io/mcp/brave-search": { - "image": "docker.io/mcp/brave-search", "digest": "sha256:ca96b8acb27d8cf601a8faef86a084602cffa41d8cb18caa1e29ba4d16989d22", + "image": "docker.io/mcp/brave-search", "pinned_image": "docker.io/mcp/brave-search@sha256:ca96b8acb27d8cf601a8faef86a084602cffa41d8cb18caa1e29ba4d16989d22" }, "ghcr.io/chopratejas/headroom:latest": { - "image": "ghcr.io/chopratejas/headroom:latest", "digest": "sha256:af709363c4f9515a88a50939baec513be13c7cd778fb6635527b104d5173cb1e", + "image": "ghcr.io/chopratejas/headroom:latest", "pinned_image": "ghcr.io/chopratejas/headroom:latest@sha256:af709363c4f9515a88a50939baec513be13c7cd778fb6635527b104d5173cb1e" }, "ghcr.io/github/gh-aw-firewall/agent-act:0.25.29": { - "image": "ghcr.io/github/gh-aw-firewall/agent-act:0.25.29", "digest": "sha256:97b4cc14dc2123a45b9d5b9927489f66882dec5857de6afc0e5bab257be92ef1", + "image": "ghcr.io/github/gh-aw-firewall/agent-act:0.25.29", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent-act:0.25.29@sha256:97b4cc14dc2123a45b9d5b9927489f66882dec5857de6afc0e5bab257be92ef1" }, "ghcr.io/github/gh-aw-firewall/agent-act:0.27.13": { - "image": "ghcr.io/github/gh-aw-firewall/agent-act:0.27.13", "digest": "sha256:676de58a0abad8054cd9d4fc45b861715856819f083a268372d60bca0090018c", + "image": "ghcr.io/github/gh-aw-firewall/agent-act:0.27.13", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent-act:0.27.13@sha256:676de58a0abad8054cd9d4fc45b861715856819f083a268372d60bca0090018c" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.18": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.18", "digest": "sha256:c77e8c26bab6c39e8568d8e2f8c17015944849a8cbcdfb4bd9725d8893725ca2", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.18", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.18@sha256:c77e8c26bab6c39e8568d8e2f8c17015944849a8cbcdfb4bd9725d8893725ca2" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.20": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.20", "digest": "sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.20", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.20@sha256:9161f2415a3306a344aca34dd671ee69f122317e0a512e66dc64c94b9c508682" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.28": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.28", "digest": "sha256:a8834e285807654bf680154faa710d43fe4365a0868142f5c20e48c85e137a7a", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.28@sha256:a8834e285807654bf680154faa710d43fe4365a0868142f5c20e48c85e137a7a" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.29": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.29", "digest": "sha256:e68f37e36962dcb3f3d1de680a49bc2302cefd001b941a7dc377155ec7ce42f4", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.29", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.29@sha256:e68f37e36962dcb3f3d1de680a49bc2302cefd001b941a7dc377155ec7ce42f4" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.40": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.40", "digest": "sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.40", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.41": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.41", "digest": "sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.41", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.41@sha256:cb2b565d070116d4b67e355775340528b5a2c3cb18b2c9049638bcc2df681770" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.66": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.66", "digest": "sha256:8373ba626ffd28ce1af503a3218205fa7744a5774479173f92e50a378a1ed00d", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.66", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.66@sha256:8373ba626ffd28ce1af503a3218205fa7744a5774479173f92e50a378a1ed00d" }, "ghcr.io/github/gh-aw-firewall/agent:0.25.67": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.67", "digest": "sha256:0f54fa48dd1a03ef6d171574eecf9a9edbf0406cea011a534cd12ed1fcb46715", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.25.67", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.25.67@sha256:0f54fa48dd1a03ef6d171574eecf9a9edbf0406cea011a534cd12ed1fcb46715" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.0": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.0", "digest": "sha256:3816d1692e6d96887b27f1e4f1d64b8d7edb43ed9d7506b8f203913cbb81c248", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.0", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.0@sha256:3816d1692e6d96887b27f1e4f1d64b8d7edb43ed9d7506b8f203913cbb81c248" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.1": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.1", "digest": "sha256:55149fa2daf8fa8afa2803f2ac1a3534591a7c96f173ee2aec9545fbe67305df", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.1", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.1@sha256:55149fa2daf8fa8afa2803f2ac1a3534591a7c96f173ee2aec9545fbe67305df" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.11": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.11", "digest": "sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.11", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.12": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.12", "digest": "sha256:a3911ae633bfc49a96be10e660d447b65023731850fee3de8a074ff8861ffde3", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.12", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.12@sha256:a3911ae633bfc49a96be10e660d447b65023731850fee3de8a074ff8861ffde3" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.13": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.13", "digest": "sha256:691a06b64961b5b35aac117eaace202fa721e91da19d1d2e22dcdd6663cd571b", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.13", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.13@sha256:691a06b64961b5b35aac117eaace202fa721e91da19d1d2e22dcdd6663cd571b" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.2": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.2", "digest": "sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.2", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.22": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.22", "digest": "sha256:55f06588411008b7148eb64b8dfe28602a0cce3675b36c6b190b54aca138468e", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.22", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.22@sha256:55f06588411008b7148eb64b8dfe28602a0cce3675b36c6b190b54aca138468e" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.26": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.26", "digest": "sha256:b283b1f037d2e068532fe178a06f2944696c3933ba11604979134e7896ac6f8c", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.26", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.26@sha256:b283b1f037d2e068532fe178a06f2944696c3933ba11604979134e7896ac6f8c" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.27": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.27", "digest": "sha256:bb5a0150dcff1cddf9b8045bb411b7759806bace0abcb132fb22158073e155d9", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.27", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.27@sha256:bb5a0150dcff1cddf9b8045bb411b7759806bace0abcb132fb22158073e155d9" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.28": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.28", "digest": "sha256:53225868cdf5600c3458bdb122d0a9d2b04a67000476bb338da03718231cd632", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.28@sha256:53225868cdf5600c3458bdb122d0a9d2b04a67000476bb338da03718231cd632" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.29": { @@ -288,113 +288,113 @@ "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.29@sha256:debc0b18ef8ea3a64585c4d1eea1099f0d9fa76b53d34a1f3c53b3225fe158fe" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.4": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.4", "digest": "sha256:b268ebf37df2428b19efcb383f001d65dc6a5ec10af43feb886d1a8477ab0e3a", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.4", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.4@sha256:b268ebf37df2428b19efcb383f001d65dc6a5ec10af43feb886d1a8477ab0e3a" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.6": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.6", "digest": "sha256:5b778c712a25397a38a47cee3467a9cbc726b16320cc133a0758c0592a6f0792", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.6", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.6@sha256:5b778c712a25397a38a47cee3467a9cbc726b16320cc133a0758c0592a6f0792" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.7": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.7", "digest": "sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.7", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c" }, "ghcr.io/github/gh-aw-firewall/agent:0.27.9": { - "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.9", "digest": "sha256:13f522853a688bfe24b04adbbe40b68101e8ef4b6fe0b636068527141bf1c269", + "image": "ghcr.io/github/gh-aw-firewall/agent:0.27.9", "pinned_image": "ghcr.io/github/gh-aw-firewall/agent:0.27.9@sha256:13f522853a688bfe24b04adbbe40b68101e8ef4b6fe0b636068527141bf1c269" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.18": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.18", "digest": "sha256:d16a40a3ca6e989896d0cef9f31b9412bb1fcc8755bafcafb95012ae1078539b", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.18", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.18@sha256:d16a40a3ca6e989896d0cef9f31b9412bb1fcc8755bafcafb95012ae1078539b" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20", "digest": "sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20@sha256:6971639e381e82e45134bcd333181f456df3a52cd6f818a3e3d6de068ff91519" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28", "digest": "sha256:93290f2393752252911bd7c39a047f776c0b53063575e7bde4e304962a9a61cb", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.28@sha256:93290f2393752252911bd7c39a047f776c0b53063575e7bde4e304962a9a61cb" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.29": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.29", "digest": "sha256:d1219e4110684402aabbeb5a43858f26790c9d0be210581cf3f7a521bd2c87b6", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.29", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.29@sha256:d1219e4110684402aabbeb5a43858f26790c9d0be210581cf3f7a521bd2c87b6" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40", "digest": "sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41", "digest": "sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41@sha256:fadd0de387209f69a9a7a1b8722bb5e7fdfb80ba9749a5c60f0e4cd7582a74d0" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.66": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.66", "digest": "sha256:4da87d0c633816cec29a26b1d1ed5fbcb0df38de19075f0429bf1d6deec3dcdc", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.66", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.66@sha256:4da87d0c633816cec29a26b1d1ed5fbcb0df38de19075f0429bf1d6deec3dcdc" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.67": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.67", "digest": "sha256:d3f51df1869bda0e1f71ae31a81450641c6ae67404e0769469aae34c2738aeb5", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.67", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.25.67@sha256:d3f51df1869bda0e1f71ae31a81450641c6ae67404e0769469aae34c2738aeb5" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.0": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.0", "digest": "sha256:f28d2bd3197fb6ef9ec40ef345bbf2bb33e50151a8e72e89abb618fc3d0066eb", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.0", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.0@sha256:f28d2bd3197fb6ef9ec40ef345bbf2bb33e50151a8e72e89abb618fc3d0066eb" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.1": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.1", "digest": "sha256:2802437f05830336ea3ae8639f628776608d14d95b5b3cf30f161eb505e29752", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.1", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.1@sha256:2802437f05830336ea3ae8639f628776608d14d95b5b3cf30f161eb505e29752" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11", "digest": "sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.12": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.12", "digest": "sha256:2329aff5e54bb5bf466ad94182eebbdfe4a4db497a9dfbe87442eb3400aa4a86", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.12", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.12@sha256:2329aff5e54bb5bf466ad94182eebbdfe4a4db497a9dfbe87442eb3400aa4a86" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.13": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.13", "digest": "sha256:c57febf4aeeefbb4fd96f5b12c07f4279ca55edca6a700032debf9dd0787286e", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.13", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.13@sha256:c57febf4aeeefbb4fd96f5b12c07f4279ca55edca6a700032debf9dd0787286e" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2", "digest": "sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.22": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.22", "digest": "sha256:afb9ff9140b17d38871dfb9dbac5ff8689ea634c2f91c435da2825192d4881c1", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.22", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.22@sha256:afb9ff9140b17d38871dfb9dbac5ff8689ea634c2f91c435da2825192d4881c1" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26", "digest": "sha256:1743dbac9bf4225f3acfdcbce4f77f5a3e61e22b2e929305525f00196693c015", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.26@sha256:1743dbac9bf4225f3acfdcbce4f77f5a3e61e22b2e929305525f00196693c015" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.27": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.27", "digest": "sha256:01e58c4383fa9952abe76e0a134a27c970f81f744d6b7861fc9e08b7964d94c3", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.27", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.27@sha256:01e58c4383fa9952abe76e0a134a27c970f81f744d6b7861fc9e08b7964d94c3" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.28": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.28", "digest": "sha256:9c409216ac1c130d2d94758586a3b9a7847dfa9606a4291354a12486e9fd370a", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.28@sha256:9c409216ac1c130d2d94758586a3b9a7847dfa9606a4291354a12486e9fd370a" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29": { @@ -403,38 +403,38 @@ "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.29@sha256:c7754df3f06f346c817db0525ba523cbdaf5349239fd7f37897c4250a8fc7bde" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.4": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.4", "digest": "sha256:3ea0d12a2d124db8ed6e2d18aff040e30ab3568161f258a132fccdeede4198cd", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.4", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.4@sha256:3ea0d12a2d124db8ed6e2d18aff040e30ab3568161f258a132fccdeede4198cd" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.6": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.6", "digest": "sha256:7b14e481f3a9898f1e9be50acc4e58541d9fcd85b49b1e4945b708f1bf1bf68e", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.6", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.6@sha256:7b14e481f3a9898f1e9be50acc4e58541d9fcd85b49b1e4945b708f1bf1bf68e" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7", "digest": "sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6" }, "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.9": { - "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.9", "digest": "sha256:9fec93937dc9d3e04f3954705c2c42f58976ebb8479b10778602631b5316e1e2", + "image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.9", "pinned_image": "ghcr.io/github/gh-aw-firewall/api-proxy:0.27.9@sha256:9fec93937dc9d3e04f3954705c2c42f58976ebb8479b10778602631b5316e1e2" }, "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26": { - "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26", "digest": "sha256:5823f6cec65210cd6e3e6320c165979fb46f78c76600f58f4189d2c89c2be8da", + "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26", "pinned_image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.26@sha256:5823f6cec65210cd6e3e6320c165979fb46f78c76600f58f4189d2c89c2be8da" }, "ghcr.io/github/gh-aw-firewall/build-tools:0.27.27": { - "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.27", "digest": "sha256:d767c00ef264ad894c73e2db192581f4a93a3297fb134aeac3e48657bb23507e", + "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.27", "pinned_image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.27@sha256:d767c00ef264ad894c73e2db192581f4a93a3297fb134aeac3e48657bb23507e" }, "ghcr.io/github/gh-aw-firewall/build-tools:0.27.28": { - "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.28", "digest": "sha256:4f9827958511e680d6a4b37910da35c154606f7efccf85c7efd76dbeb22481a2", + "image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.28@sha256:4f9827958511e680d6a4b37910da35c154606f7efccf85c7efd76dbeb22481a2" }, "ghcr.io/github/gh-aw-firewall/build-tools:0.27.29": { @@ -443,78 +443,78 @@ "pinned_image": "ghcr.io/github/gh-aw-firewall/build-tools:0.27.29@sha256:fbedcc57550fcbb0ad386fda8028ad77172ee505f53af9a8d8a7b8e1644b0e38" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28", "digest": "sha256:fdf310e4678ce58d248c466b89399e9680a3003038fd19322c388559016aaac7", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.28@sha256:fdf310e4678ce58d248c466b89399e9680a3003038fd19322c388559016aaac7" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.29": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.29", "digest": "sha256:29917488eb90a01ff9544ffeeb5cc26434a8ea16d69ae8972f5f6be0e567e276", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.29", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.29@sha256:29917488eb90a01ff9544ffeeb5cc26434a8ea16d69ae8972f5f6be0e567e276" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.40": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.40", "digest": "sha256:3e7152911d4b4b7b97beef9d3d7d924ff7902227e86001ef3838fb728d5d514c", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.40", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.40@sha256:3e7152911d4b4b7b97beef9d3d7d924ff7902227e86001ef3838fb728d5d514c" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.41": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.41", "digest": "sha256:62171f2fa508667b8b0a9e096f826983f312e3da0ce894f80c0f83a875af60fe", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.41", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.41@sha256:62171f2fa508667b8b0a9e096f826983f312e3da0ce894f80c0f83a875af60fe" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.66": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.66", "digest": "sha256:ff53f2b227ceaf7334baeab5daa2537211089aca251af9630cb0b5a8899492d0", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.66", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.66@sha256:ff53f2b227ceaf7334baeab5daa2537211089aca251af9630cb0b5a8899492d0" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.67": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.67", "digest": "sha256:97387002ec54c8ab9f255d5aaf3d435071642e058f528c8268ca0e80b201609a", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.67", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.67@sha256:97387002ec54c8ab9f255d5aaf3d435071642e058f528c8268ca0e80b201609a" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.0": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.0", "digest": "sha256:42529ecb9f90da5adb00593d268dfdbd35d14bb1dc92dd897286b27ce1e3d58d", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.0", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.0@sha256:42529ecb9f90da5adb00593d268dfdbd35d14bb1dc92dd897286b27ce1e3d58d" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.1": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.1", "digest": "sha256:2e6dc98321dbf82840f83ec0ef8b198506149255a15d3a7854d59c0d34063e27", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.1", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.1@sha256:2e6dc98321dbf82840f83ec0ef8b198506149255a15d3a7854d59c0d34063e27" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.12": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.12", "digest": "sha256:7d267199839cc0f23c01fd39f8c050a5d86cb6cc5058aa829cd472f405fb1a1d", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.12", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.12@sha256:7d267199839cc0f23c01fd39f8c050a5d86cb6cc5058aa829cd472f405fb1a1d" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.13": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.13", "digest": "sha256:79dfd3a5d139bd1956ba6d7d1782b831a07175cf5afa29c45cb20bb0140f23c5", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.13", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.13@sha256:79dfd3a5d139bd1956ba6d7d1782b831a07175cf5afa29c45cb20bb0140f23c5" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.2": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.2", "digest": "sha256:02f3ec08f32dc26c5427920c6a2e2f3036238fce44802f2f11ef49ed8621b5d0", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.2", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.2@sha256:02f3ec08f32dc26c5427920c6a2e2f3036238fce44802f2f11ef49ed8621b5d0" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.22": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.22", "digest": "sha256:e23e1604241f579b418e6522d938285b57ada31bc27742a65c90ee2250b1755c", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.22", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.22@sha256:e23e1604241f579b418e6522d938285b57ada31bc27742a65c90ee2250b1755c" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26", "digest": "sha256:6006b1c579ca550e023697b5dfd832ae03361328a4c0f2eb49fb181a6b8d7a4b", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.26@sha256:6006b1c579ca550e023697b5dfd832ae03361328a4c0f2eb49fb181a6b8d7a4b" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.27": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.27", "digest": "sha256:70df326caf73bf5911340dca4620b529a483dd8f42142b0a41d7b9761ab4ab7a", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.27", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.27@sha256:70df326caf73bf5911340dca4620b529a483dd8f42142b0a41d7b9761ab4ab7a" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.28": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.28", "digest": "sha256:65f9b00a2dfa9a093a3e65de8259ba37d962b12ce1dfec15670f005961cddad4", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.28@sha256:65f9b00a2dfa9a093a3e65de8259ba37d962b12ce1dfec15670f005961cddad4" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29": { @@ -523,113 +523,113 @@ "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.29@sha256:bfc90b1f10d2ff61dbbbf0d57600001f85bf5f6444ed78bc05d9f6677327e4b8" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.4": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.4", "digest": "sha256:72c378c029d2fad4684847ab44c329e526ac6b1a78cdf97656870ea11d201545", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.4", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.4@sha256:72c378c029d2fad4684847ab44c329e526ac6b1a78cdf97656870ea11d201545" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.6": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.6", "digest": "sha256:194b21f5d3284b0b2abf2603a14ec607f89d798165a7ef453667706c69401735", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.6", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.6@sha256:194b21f5d3284b0b2abf2603a14ec607f89d798165a7ef453667706c69401735" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7", "digest": "sha256:4757f198a3fa20f88bdbe70be7ae1a05f127d9c0a9e96a5d6460ef40c08fc83d", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7@sha256:4757f198a3fa20f88bdbe70be7ae1a05f127d9c0a9e96a5d6460ef40c08fc83d" }, "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.9": { - "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.9", "digest": "sha256:89b715de241408e03d35d5c4851361ae350f69aa56d7955dd2ed1d6d3ce037cc", + "image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.9", "pinned_image": "ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.9@sha256:89b715de241408e03d35d5c4851361ae350f69aa56d7955dd2ed1d6d3ce037cc" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.18": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.18", "digest": "sha256:eb102afcfbae26ffcec016adebb74d3be7b0a5bf376ba306599cdf3effbe288e", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.18", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.18@sha256:eb102afcfbae26ffcec016adebb74d3be7b0a5bf376ba306599cdf3effbe288e" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.20": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.20", "digest": "sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.20", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.20@sha256:5411d903f73ee597e6a084971c2adef3eb0bd405910df3ed7bf5e3d6bd58a236" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.28": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.28", "digest": "sha256:844c18280f82cd1b06345eb2f4e91966b34185bfc51c9f237c3e022e848fb474", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.28@sha256:844c18280f82cd1b06345eb2f4e91966b34185bfc51c9f237c3e022e848fb474" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.29": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.29", "digest": "sha256:8a71ad9e40454051672312917e51567abfb8251d7c294d086c48f63d84e4cb53", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.29", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.29@sha256:8a71ad9e40454051672312917e51567abfb8251d7c294d086c48f63d84e4cb53" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.40": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.40", "digest": "sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.40", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.41": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.41", "digest": "sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.41", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.41@sha256:1260445d25968dbf3ae70143964177a0e5914cf2ce07a6117f7d3caec6c3e3c4" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.66": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.66", "digest": "sha256:9c47e6d0210c5b1581b56d1fe8ac32c1d8fdd7cb04ef95592f2431e59f533bd3", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.66", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.66@sha256:9c47e6d0210c5b1581b56d1fe8ac32c1d8fdd7cb04ef95592f2431e59f533bd3" }, "ghcr.io/github/gh-aw-firewall/squid:0.25.67": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.67", "digest": "sha256:9a05085db054f41bd67c772bcfc25cabc15bc33ee993b051a31e30669dd2031f", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.25.67", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.25.67@sha256:9a05085db054f41bd67c772bcfc25cabc15bc33ee993b051a31e30669dd2031f" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.0": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.0", "digest": "sha256:d6a01d4cf3d928e6a7fc42e34afef228e753dce87646edc91d8a5cd0b612d9a6", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.0", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.0@sha256:d6a01d4cf3d928e6a7fc42e34afef228e753dce87646edc91d8a5cd0b612d9a6" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.1": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.1", "digest": "sha256:1f3df3207dc9faa9080088115ca50a5ab0d7a692c61dffa8c8898d0b7b750413", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.1", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.1@sha256:1f3df3207dc9faa9080088115ca50a5ab0d7a692c61dffa8c8898d0b7b750413" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.11": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.11", "digest": "sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.11", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.12": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.12", "digest": "sha256:f5d9403364543b23a8510278854cf5517345419daed19de044a7130c03388484", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.12", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.12@sha256:f5d9403364543b23a8510278854cf5517345419daed19de044a7130c03388484" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.13": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.13", "digest": "sha256:700b1b5a73098373b04fb684f291e95d9be0124ab559717b04f27acaf8b41bed", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.13", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.13@sha256:700b1b5a73098373b04fb684f291e95d9be0124ab559717b04f27acaf8b41bed" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.2": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.2", "digest": "sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.2", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.22": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.22", "digest": "sha256:3cdcc1e2b4b4fe602ba69fd3e21aac7ac512d5c1fce24df4ce69dc4f98164b59", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.22", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.22@sha256:3cdcc1e2b4b4fe602ba69fd3e21aac7ac512d5c1fce24df4ce69dc4f98164b59" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.26": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.26", "digest": "sha256:d4647c3bfaf80889eb1dbd3d4e2063340cbf94c0ca6c5747bbfc8507b12f3485", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.26", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.26@sha256:d4647c3bfaf80889eb1dbd3d4e2063340cbf94c0ca6c5747bbfc8507b12f3485" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.27": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.27", "digest": "sha256:92d820df47b2eff75d93a5bec4dc183a3ec55ed7ddb4f25cb0fdda5c3e995409", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.27", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.27@sha256:92d820df47b2eff75d93a5bec4dc183a3ec55ed7ddb4f25cb0fdda5c3e995409" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.28": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.28", "digest": "sha256:41d20c773ec155c5ff02435fe048bdca497834f559d45d2126ce1ba8a9c83963", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.28", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.28@sha256:41d20c773ec155c5ff02435fe048bdca497834f559d45d2126ce1ba8a9c83963" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.29": { @@ -638,233 +638,233 @@ "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.29@sha256:7bfa0742f9a5bd6309507caaa80a8b6cf3e05bd95a1429affbf64cc94cfbd34f" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.4": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.4", "digest": "sha256:87979038897e40caed22245b64d1daa796390d2dca289b99d3d1174c85740af8", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.4", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.4@sha256:87979038897e40caed22245b64d1daa796390d2dca289b99d3d1174c85740af8" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.6": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.6", "digest": "sha256:730985e67931b9774545bce76b3ac5a354aa1dc11f19ee8f2d9cbf3211d73c3a", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.6", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.6@sha256:730985e67931b9774545bce76b3ac5a354aa1dc11f19ee8f2d9cbf3211d73c3a" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.7": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.7", "digest": "sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.7", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96" }, "ghcr.io/github/gh-aw-firewall/squid:0.27.9": { - "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.9", "digest": "sha256:f186085bd04864e59f5057ff9e6bbd506475275ffe165111517d7d1acdc1de9c", + "image": "ghcr.io/github/gh-aw-firewall/squid:0.27.9", "pinned_image": "ghcr.io/github/gh-aw-firewall/squid:0.27.9@sha256:f186085bd04864e59f5057ff9e6bbd506475275ffe165111517d7d1acdc1de9c" }, "ghcr.io/github/gh-aw-mcpg:v0.2.19": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.2.19", "digest": "sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd", + "image": "ghcr.io/github/gh-aw-mcpg:v0.2.19", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.2.19@sha256:44d4d8de7e6c37aaea484eba489940c52df6a0b54078ddcbc9327592d5b3c3dd" }, "ghcr.io/github/gh-aw-mcpg:v0.2.30": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.2.30", "digest": "sha256:e950e6d39f003862d33bfb8d4eb93e242d919cf6ca874b90728e5e0ea7434c6f", + "image": "ghcr.io/github/gh-aw-mcpg:v0.2.30", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.2.30@sha256:e950e6d39f003862d33bfb8d4eb93e242d919cf6ca874b90728e5e0ea7434c6f" }, "ghcr.io/github/gh-aw-mcpg:v0.3.0": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.0", "digest": "sha256:9c2228324fb1f26f39dc9471612e530ae3efc3156dac05efb2e8d212878d454d", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.0", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.0@sha256:9c2228324fb1f26f39dc9471612e530ae3efc3156dac05efb2e8d212878d454d" }, "ghcr.io/github/gh-aw-mcpg:v0.3.1": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.1", "digest": "sha256:287fad0236959f3b3d9936ea1ef8d5b4f135ef2a5f5789713495cbbef191e60c", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.1", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.1@sha256:287fad0236959f3b3d9936ea1ef8d5b4f135ef2a5f5789713495cbbef191e60c" }, "ghcr.io/github/gh-aw-mcpg:v0.3.16": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.16", "digest": "sha256:8001e4bfa52d45abd05c45a8f855ce62bc893eb66e4807bb487bf2ff07fc1473", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.16", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.16@sha256:8001e4bfa52d45abd05c45a8f855ce62bc893eb66e4807bb487bf2ff07fc1473" }, "ghcr.io/github/gh-aw-mcpg:v0.3.23": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.23", "digest": "sha256:0dd1bd91a41e24a3ccc31b1ec6cb61d36608997fabf91f2d643b64e3fc33180a", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.23", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.23@sha256:0dd1bd91a41e24a3ccc31b1ec6cb61d36608997fabf91f2d643b64e3fc33180a" }, "ghcr.io/github/gh-aw-mcpg:v0.3.24": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.24", "digest": "sha256:72cc921901afa1d67b6fee568f110c6b84436624c437fb2996d14c83e90a2b54", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.24", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.24@sha256:72cc921901afa1d67b6fee568f110c6b84436624c437fb2996d14c83e90a2b54" }, "ghcr.io/github/gh-aw-mcpg:v0.3.25": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.25", "digest": "sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.25", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa" }, "ghcr.io/github/gh-aw-mcpg:v0.3.26": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.26", "digest": "sha256:d3b03f54eee3a8176818c9a52087623e45b7f644a28814337fcc0838e2534490", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.26", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.26@sha256:d3b03f54eee3a8176818c9a52087623e45b7f644a28814337fcc0838e2534490" }, "ghcr.io/github/gh-aw-mcpg:v0.3.27": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.27", "digest": "sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.27", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7" }, "ghcr.io/github/gh-aw-mcpg:v0.3.29": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.29", "digest": "sha256:f24e968e6e5aeb819e9a98b9d273efe36b0fc1a0cb17e9ace263b5ab20de87cd", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.29", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.29@sha256:f24e968e6e5aeb819e9a98b9d273efe36b0fc1a0cb17e9ace263b5ab20de87cd" }, "ghcr.io/github/gh-aw-mcpg:v0.3.30": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.30", "digest": "sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.30", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be" }, "ghcr.io/github/gh-aw-mcpg:v0.3.31": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.31", "digest": "sha256:3147d7332939aeb2bc6a4220d96f6a0ca600b5cc0971200eca3d1e948fc8122a", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.31", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.31@sha256:3147d7332939aeb2bc6a4220d96f6a0ca600b5cc0971200eca3d1e948fc8122a" }, "ghcr.io/github/gh-aw-mcpg:v0.3.32": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.32", "digest": "sha256:63e46b56dfd70895a701b6fc6dd0189e11e2d875f327f1781e81b31848735477", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.32", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.32@sha256:63e46b56dfd70895a701b6fc6dd0189e11e2d875f327f1781e81b31848735477" }, "ghcr.io/github/gh-aw-mcpg:v0.3.33": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.33", "digest": "sha256:f0e1f1d562f01e737f3ef125cb4d7057ff87a68a97927f5ea9fd6d3f5091da06", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.33", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.33@sha256:f0e1f1d562f01e737f3ef125cb4d7057ff87a68a97927f5ea9fd6d3f5091da06" }, "ghcr.io/github/gh-aw-mcpg:v0.3.6": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.6", "digest": "sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.6", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c" }, "ghcr.io/github/gh-aw-mcpg:v0.3.8": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.8", "digest": "sha256:71e102af76e1ce10b455d2a2ef5717d9cc4c135a5db06fbadcb58e6c1a950cd4", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.8", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.8@sha256:71e102af76e1ce10b455d2a2ef5717d9cc4c135a5db06fbadcb58e6c1a950cd4" }, "ghcr.io/github/gh-aw-mcpg:v0.3.9": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.3.9", "digest": "sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388", + "image": "ghcr.io/github/gh-aw-mcpg:v0.3.9", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388" }, "ghcr.io/github/gh-aw-mcpg:v0.4.0": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.4.0", "digest": "sha256:9dbdf42842c224a95016df1d2a85a2901e04204c242079343b302a307d2b8031", + "image": "ghcr.io/github/gh-aw-mcpg:v0.4.0", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.4.0@sha256:9dbdf42842c224a95016df1d2a85a2901e04204c242079343b302a307d2b8031" }, "ghcr.io/github/gh-aw-mcpg:v0.4.1": { - "image": "ghcr.io/github/gh-aw-mcpg:v0.4.1", "digest": "sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32", + "image": "ghcr.io/github/gh-aw-mcpg:v0.4.1", "pinned_image": "ghcr.io/github/gh-aw-mcpg:v0.4.1@sha256:ad2a979c2cd8b50098e84938ca9c9c1580eb8e91526f101a90adfba7859b2c32" }, "ghcr.io/github/gh-aw-node": { - "image": "ghcr.io/github/gh-aw-node", "digest": "sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b", + "image": "ghcr.io/github/gh-aw-node", "pinned_image": "ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b" }, "ghcr.io/github/github-mcp-server:v0.32.0": { - "image": "ghcr.io/github/github-mcp-server:v0.32.0", "digest": "sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28", + "image": "ghcr.io/github/github-mcp-server:v0.32.0", "pinned_image": "ghcr.io/github/github-mcp-server:v0.32.0@sha256:2763823c63bcca718ce53850a1d7fcf2f501ec84028394f1b63ce7e9f4f9be28" }, "ghcr.io/github/github-mcp-server:v1.0.0": { - "image": "ghcr.io/github/github-mcp-server:v1.0.0", "digest": "sha256:d2550953f8050bc5a1c8f80d1678766f66f60bbfbcd953fdeaf661fe4269bd95", + "image": "ghcr.io/github/github-mcp-server:v1.0.0", "pinned_image": "ghcr.io/github/github-mcp-server:v1.0.0@sha256:d2550953f8050bc5a1c8f80d1678766f66f60bbfbcd953fdeaf661fe4269bd95" }, "ghcr.io/github/github-mcp-server:v1.0.3": { - "image": "ghcr.io/github/github-mcp-server:v1.0.3", "digest": "sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959", + "image": "ghcr.io/github/github-mcp-server:v1.0.3", "pinned_image": "ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959" }, "ghcr.io/github/github-mcp-server:v1.0.4": { - "image": "ghcr.io/github/github-mcp-server:v1.0.4", "digest": "sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4", + "image": "ghcr.io/github/github-mcp-server:v1.0.4", "pinned_image": "ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4" }, "ghcr.io/github/github-mcp-server:v1.1.0": { - "image": "ghcr.io/github/github-mcp-server:v1.1.0", "digest": "sha256:71b07d9abecb83b4a2595bcd8ccb35f9a0166361a12335f9e16da1ef07172029", + "image": "ghcr.io/github/github-mcp-server:v1.1.0", "pinned_image": "ghcr.io/github/github-mcp-server:v1.1.0@sha256:71b07d9abecb83b4a2595bcd8ccb35f9a0166361a12335f9e16da1ef07172029" }, "ghcr.io/github/github-mcp-server:v1.1.2": { - "image": "ghcr.io/github/github-mcp-server:v1.1.2", "digest": "sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c", + "image": "ghcr.io/github/github-mcp-server:v1.1.2", "pinned_image": "ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c" }, "ghcr.io/github/github-mcp-server:v1.3.0": { - "image": "ghcr.io/github/github-mcp-server:v1.3.0", "digest": "sha256:5c83359327a0bacc3d34db730bea6557d39d341cee0bf6c58c9a896e33150e80", + "image": "ghcr.io/github/github-mcp-server:v1.3.0", "pinned_image": "ghcr.io/github/github-mcp-server:v1.3.0@sha256:5c83359327a0bacc3d34db730bea6557d39d341cee0bf6c58c9a896e33150e80" }, "ghcr.io/github/github-mcp-server:v1.4.0": { - "image": "ghcr.io/github/github-mcp-server:v1.4.0", "digest": "sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036", + "image": "ghcr.io/github/github-mcp-server:v1.4.0", "pinned_image": "ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036" }, "ghcr.io/github/github-mcp-server:v1.5.0": { - "image": "ghcr.io/github/github-mcp-server:v1.5.0", "digest": "sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4", + "image": "ghcr.io/github/github-mcp-server:v1.5.0", "pinned_image": "ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4" }, "ghcr.io/github/serena-mcp-server:latest": { - "image": "ghcr.io/github/serena-mcp-server:latest", "digest": "sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5", + "image": "ghcr.io/github/serena-mcp-server:latest", "pinned_image": "ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5" }, "grafana/mcp-grafana": { - "image": "grafana/mcp-grafana", "digest": "sha256:60a4e3a417a69eeb864a72052c53b4aa4466ff3577d6ef9bacc671f4b77d7090", + "image": "grafana/mcp-grafana", "pinned_image": "grafana/mcp-grafana@sha256:60a4e3a417a69eeb864a72052c53b4aa4466ff3577d6ef9bacc671f4b77d7090" }, "mcp/arxiv-mcp-server": { - "image": "mcp/arxiv-mcp-server", "digest": "sha256:6dc6bba6dfed97f4ad6eb8d23a5c98ef5b7fa6184937d54b2d675801cd9dd29e", + "image": "mcp/arxiv-mcp-server", "pinned_image": "mcp/arxiv-mcp-server@sha256:6dc6bba6dfed97f4ad6eb8d23a5c98ef5b7fa6184937d54b2d675801cd9dd29e" }, "mcp/ast-grep:latest": { - "image": "mcp/ast-grep:latest", "digest": "sha256:5fc3f2e9dcf2c019e92662f608b8d89e12134ed6d91e6f5461de6efd506a1e72", + "image": "mcp/ast-grep:latest", "pinned_image": "mcp/ast-grep:latest@sha256:5fc3f2e9dcf2c019e92662f608b8d89e12134ed6d91e6f5461de6efd506a1e72" }, "mcp/context7": { - "image": "mcp/context7", "digest": "sha256:1174e6a29634a83b2be93ac1fefabf63265f498c02c72201fe3464e687dd8836", + "image": "mcp/context7", "pinned_image": "mcp/context7@sha256:1174e6a29634a83b2be93ac1fefabf63265f498c02c72201fe3464e687dd8836" }, "mcp/markitdown": { - "image": "mcp/markitdown", "digest": "sha256:1cef3bf502503310ed0884441874ccf6cdaac20136dc1179797fa048269dc4cb", + "image": "mcp/markitdown", "pinned_image": "mcp/markitdown@sha256:1cef3bf502503310ed0884441874ccf6cdaac20136dc1179797fa048269dc4cb" }, "mcp/memory": { - "image": "mcp/memory", "digest": "sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f", + "image": "mcp/memory", "pinned_image": "mcp/memory@sha256:db0c2db07a44b6797eba7a832b1bda142ffc899588aae82c92780cbb2252407f" }, "mcp/notion": { - "image": "mcp/notion", "digest": "sha256:4de8eb0de33402fcbd3740b4f4039918e4893155c7ea833c7a0c472001b88367", + "image": "mcp/notion", "pinned_image": "mcp/notion@sha256:4de8eb0de33402fcbd3740b4f4039918e4893155c7ea833c7a0c472001b88367" }, "mcr.microsoft.com/playwright/mcp": { - "image": "mcr.microsoft.com/playwright/mcp", "digest": "sha256:7b82f29c6ef83480a97f612d53ac3fd5f30a32df3fea1e06923d4204d3532bb2", + "image": "mcr.microsoft.com/playwright/mcp", "pinned_image": "mcr.microsoft.com/playwright/mcp@sha256:7b82f29c6ef83480a97f612d53ac3fd5f30a32df3fea1e06923d4204d3532bb2" }, "node:lts-alpine": { - "image": "node:lts-alpine", "digest": "sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14", + "image": "node:lts-alpine", "pinned_image": "node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14" }, "python:alpine": { - "image": "python:alpine", "digest": "sha256:6f873e340e6786787a632c919ecfb1d2301eb33ccfbe9f0d0add16cbc0892116", + "image": "python:alpine", "pinned_image": "python:alpine@sha256:6f873e340e6786787a632c919ecfb1d2301eb33ccfbe9f0d0add16cbc0892116" }, "semgrep/semgrep:latest": { - "image": "semgrep/semgrep:latest", "digest": "sha256:17d89ddd91a7729bbd5de09402f7f79a70204289e2a94635086e9db532a495f2", + "image": "semgrep/semgrep:latest", "pinned_image": "semgrep/semgrep:latest@sha256:17d89ddd91a7729bbd5de09402f7f79a70204289e2a94635086e9db532a495f2" } } diff --git a/pkg/workflow/safe_outputs_config_global.go b/pkg/workflow/safe_outputs_config_global.go index 8dccbc5340e..d5d50e19a64 100644 --- a/pkg/workflow/safe_outputs_config_global.go +++ b/pkg/workflow/safe_outputs_config_global.go @@ -92,6 +92,12 @@ func (c *Compiler) extractGlobalConfigFields(outputMap map[string]any, config *S config.ThreatDetection = threatDetectionConfig } + // Handle content-redaction + contentRedactionConfig := c.parseContentRedactionConfig(outputMap) + if contentRedactionConfig != nil { + config.ContentRedaction = contentRedactionConfig + } + // Handle runs-on configuration if runsOn, exists := outputMap["runs-on"]; exists { config.RunsOn = renderRunsOnSnippet(runsOn) diff --git a/pkg/workflow/safe_outputs_config_types.go b/pkg/workflow/safe_outputs_config_types.go index 911cc814089..81dcb6833ba 100644 --- a/pkg/workflow/safe_outputs_config_types.go +++ b/pkg/workflow/safe_outputs_config_types.go @@ -85,6 +85,7 @@ type SafeOutputsConfig struct { NoOp *NoOpConfig `yaml:"noop,omitempty"` // No-op output for logging only (always available as fallback) ReportIncomplete *ReportIncompleteConfig `yaml:"report-incomplete,omitempty"` // Signal that the task could not be completed due to a tool or infrastructure failure ThreatDetection *ThreatDetectionConfig `yaml:"threat-detection,omitempty"` // Threat detection configuration + ContentRedaction *ContentRedactionConfig `yaml:"content-redaction,omitempty"` // Content redaction configuration for public-facing outputs Jobs map[string]*SafeJobConfig `yaml:"jobs,omitempty"` // Safe-jobs configuration (moved from top-level) Scripts map[string]*SafeScriptConfig `yaml:"scripts,omitempty"` // Custom inline handlers that run in the safe-output handler loop GitHubApp *GitHubAppConfig `yaml:"github-app,omitempty"` // GitHub App credentials for token minting @@ -172,3 +173,37 @@ type MentionsConfig struct { type SecretMaskingConfig struct { Steps []map[string]any `yaml:"steps,omitempty"` // Additional secret redaction steps to inject after built-in redaction } + +// ContentRedactionConfig holds configuration for content redaction of public-facing outputs. +// A fresh-context subagent — no tools, no repo access, no MCP — reviews all text-bearing +// output items and rewrites non-compliant text or blocks items that cannot be made compliant. +type ContentRedactionConfig struct { + // Policies is the list of policy sources concatenated into the redaction agent's system prompt. + // Each entry may be a URL (fetched at runtime via curl), a repo-relative path + // (read from the checkout), or an inline string literal. + // Required. Accepts a single string or an array of strings. + Policies []string `yaml:"-"` // parsed from scalar or array; see parseContentRedactionConfig + + // Model is an optional model override for the redaction agent. + Model string `yaml:"model,omitempty"` + + // OnFailure controls behaviour when the redaction agent determines an item cannot be made + // compliant. "block" (default) removes the item from the output. "warn" keeps the item + // and emits a step summary warning. + OnFailure string `yaml:"on-failure,omitempty"` + + // Scope restricts redaction to specific safe-output types. When nil or empty, + // all text-bearing output types are redacted. + Scope []string `yaml:"scope,omitempty"` + + // RunsOn is an optional runner override for the content_redaction job. + RunsOn string `yaml:"runs-on,omitempty"` + + // ContinueOnError controls whether a failed redaction job blocks safe_outputs. + // Defaults to false (failures block downstream jobs). + ContinueOnError *bool `yaml:"continue-on-error,omitempty"` + + // IfExpr is the expression form of the if conditional when a GitHub Actions + // expression was provided (e.g. "${{ inputs.enable-content-redaction }}"). + IfExpr *string `yaml:"-"` +}