Skip to content

fix: harden credentialed shared actions - #176

Open
aeneasr wants to merge 2 commits into
masterfrom
fix/harden-keto-shared-actions
Open

fix: harden credentialed shared actions#176
aeneasr wants to merge 2 commits into
masterfrom
fix/harden-keto-shared-actions

Conversation

@aeneasr

@aeneasr aeneasr commented Aug 11, 2026

Copy link
Copy Markdown
Member

Summary

  • pin nested actions, the changelog input, release tools, the newsletter CLI, and the GoReleaser image to immutable revisions
  • stop persisting release checkout credentials and expose publication secrets only to protected operations
  • add a reusable CLI-docs workflow that generates and formats without the docs PAT, then validates and publishes in a separate job
  • make the newsletter SSH key optional and add a focused immutable-dependency regression check

Verification

  • actionlint for the changed workflows and YAML parsing for all changed action manifests
  • positive and negative immutable-reference checks, including mutable action tags and Docker images
  • ShellCheck and shfmt for the affected shell code
  • all 14 conventional-commit configuration tests and the existing licenses tests
  • generated and formatted 23 Keto CLI Markdown files through the new credential-free path
  • rendered Keto release notes from the pinned changelog revision
  • verified the newsletter CLI archive checksum and pinned GoReleaser image
  • git diff --check

Local macOS note: the authors and changelog Make targets download a Linux ShellCheck binary, so their ShellCheck and shfmt commands were run directly with native tools.

Summary by CodeRabbit

  • Chores
    • Pinned CI/CD action references to immutable revisions for improved reliability and reproducibility.
    • Locked installer and checkout sources to specific commits instead of floating branches or tags.
    • Replaced the floating GoReleaser image tag with a versioned image digest.
    • Preserved existing build, release, checkout, and notification behavior.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6e14829a-7eb7-420c-ab26-a5fc0c44d92b

📥 Commits

Reviewing files that changed from the base of the PR and between 7e6bbd1 and 255d0b3.

📒 Files selected for processing (4)
  • .github/workflows/test.yml
  • checkout/action.yml
  • newsletter/action.yml
  • releaser/action.yml
📝 Walkthrough

Walkthrough

The pull request adds a reusable CLI documentation publishing workflow and hardens existing workflows and composite actions. External actions and release tools use pinned revisions. Checkout credentials, permissions, shell handling, artifact validation, and conditional publishing are updated.

Changes

CLI documentation publishing

Layer / File(s) Summary
Documentation generation and validation
.github/workflows/docs-cli-next.yml
Defines reusable inputs and secrets. Generates CLI Markdown, formats it, validates its contents, and uploads an artifact.
Documentation branch publication
.github/workflows/docs-cli-next.yml
Downloads and revalidates the artifact, replaces the output directory, and prepares a commit only when content changes.
Conditional documentation push
.github/workflows/docs-cli-next.yml
Pushes changed documentation to the configured branch for master branches and tags.

Action and workflow hardening

Layer / File(s) Summary
Immutable action reference validation
.github/workflows/test.yml
Adds read-only contents permissions and rejects mutable external action references.
Pinned checkout configuration
checkout/action.yml
Pins both checkout paths to the actions/checkout v7 commit.
Newsletter execution hardening
newsletter/action.yml, newsletter/slack-notify/action.yml
Pins checkout and Slack actions, verifies the Ory CLI archive checksum, makes SSH setup conditional, and enables strict shell handling.

Release action hardening

Layer / File(s) Summary
Pinned release tooling
releaser/action.yml
Pins release tools and containers, upgrades Node to 24, adds build tooling, and updates changelog handling.
Release artifact commits
releaser/action.yml
Separates artifact commits and uses authenticated repository URLs for release pushes.
Post-release documentation publication
releaser/action.yml
Adds a publication output and conditionally commits documentation changes to master.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SourceRepository
  participant GenerateJob
  participant DocumentationRepository
  participant ArtifactStorage
  participant PublishJob
  SourceRepository->>GenerateJob: run CLI documentation generator
  GenerateJob->>DocumentationRepository: write generated Markdown
  GenerateJob->>ArtifactStorage: upload validated artifact
  PublishJob->>ArtifactStorage: download artifact
  PublishJob->>DocumentationRepository: revalidate and replace output directory
  PublishJob->>DocumentationRepository: rebase, commit, and push changes
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: hardening credentialed shared actions and workflows.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/harden-keto-shared-actions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (1)
.github/workflows/docs-cli-next.yml (1)

84-87: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Use committed integrity data for formatter dependencies.

These commands resolve package content from the registry during each run and do not use committed integrity data. Use a lockfile with npm ci, or verify the expected SHA-512 checksums before extraction and execution.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docs-cli-next.yml around lines 84 - 87, Update the
formatter dependency setup in the workflow to use committed integrity data
instead of resolving unverified packages at runtime: replace the npm
pack/install flow involving ory-prettier-styles and global prettier with a
lockfile-driven npm ci installation, or verify the expected SHA-512 checksums
before extracting and executing the packages.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/docs-cli-next.yml:
- Around line 171-185: Update the “Push generated docs” step to handle a
non-fast-forward push by rebasing the existing local commit onto the latest
DOCS_BRANCH and retrying the push. Keep the normal push path unchanged, and
abort safely without force-pushing if the rebase encounters conflicts.

In @.github/workflows/test.yml:
- Around line 31-37: Replace the hard-coded files array in the workflow
validation step with discovery of all YAML workflow files under
.github/workflows and all action.yml/action.yaml manifests, including
labels.yml. Pass the complete discovered file set to the immutable-dependency
check so every workflow and composite-action manifest is scanned.

In `@newsletter/action.yml`:
- Around line 39-41: Add the requested curl timeout options to the download
command in the newsletter action: use a 15-second connection timeout, 120-second
maximum transfer time, and 300-second maximum retry duration, while preserving
the existing retry count and other flags.
- Around line 53-58: Update the SSH setup around ssh-keyscan and the global
core.sshCommand configuration to validate github.com against GitHub’s published
host key or a pinned fingerprint before writing known_hosts. Configure
core.sshCommand with StrictHostKeyChecking=yes and a job-local
UserKnownHostsFile, while preserving the existing key and ssh-agent setup.
- Around line 20-28: Update the README usage example for the newsletter action
to declare the required caller permission, adding contents: read under
permissions. Keep the existing example configuration and checkout behavior
unchanged.
- Around line 49-58: Update the SSH setup run block to store the key,
known_hosts, and related files under unique paths in $RUNNER_TEMP, and configure
core.sshCommand locally rather than globally. Remove ssh-agent startup and
ssh-add unless the socket and PID are explicitly passed to all required steps.
Add a separate cleanup step with if: ${{ always() }} that removes the temporary
key and SSH files and stops any started agent.

In `@releaser/action.yml`:
- Around line 127-135: Remove blanket “|| true” suppression from the publication
Git commands in releaser/action.yml lines 127-135, allowing stash restoration,
checkout master, pull/rebase, and push failures to stop the action; handle only
the expected no-change commit result explicitly. Apply the same failure-handling
correction to the push flow at releaser/action.yml lines 163-170 before pushing
HEAD:master.
- Around line 149-155: Update the pre-release check in the release workflow to
inspect the triggering release ref `${GITHUB_REF_NAME}` rather than relying on
`git describe --tags` from the worktree switched to master. Preserve the
existing skip message and exit behavior for pre-release refs, while allowing
documentation publishing for regular releases.

---

Nitpick comments:
In @.github/workflows/docs-cli-next.yml:
- Around line 84-87: Update the formatter dependency setup in the workflow to
use committed integrity data instead of resolving unverified packages at
runtime: replace the npm pack/install flow involving ory-prettier-styles and
global prettier with a lockfile-driven npm ci installation, or verify the
expected SHA-512 checksums before extracting and executing the packages.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9ffeadab-2337-4181-be92-656061495830

📥 Commits

Reviewing files that changed from the base of the PR and between 53206fb and 7e6bbd1.

📒 Files selected for processing (6)
  • .github/workflows/docs-cli-next.yml
  • .github/workflows/test.yml
  • checkout/action.yml
  • newsletter/action.yml
  • newsletter/slack-notify/action.yml
  • releaser/action.yml

Comment thread .github/workflows/docs-cli-next.yml Outdated
Comment on lines +171 to +185
git stash
git pull --rebase origin "$DOCS_BRANCH"
git stash apply
git add -- "$OUTPUT_DIR"
git commit -m "autogen(docs): generate cli docs"
echo "changed=true" >> "$GITHUB_OUTPUT"
- name: Push generated docs
if: ${{ steps.commit.outputs.changed == 'true' }}
working-directory: docs
env:
DOCS_BRANCH: ${{ inputs.docs-branch }}
DOCS_TOKEN: ${{ secrets.token }}
run: |
git push "https://x-access-token:${DOCS_TOKEN}@github.com/ory/docs.git" \
"HEAD:${DOCS_BRANCH}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Retry after a non-fast-forward push failure.

The rebase occurs before the local commit exists. If two runs start from the same docs-branch tip, one push succeeds and the other push fails. Rebase the committed change onto the latest remote branch and retry the push. Fail safely if the rebase conflicts.

Proposed fix
-          git push "https://x-access-token:${DOCS_TOKEN}`@github.com/ory/docs.git`" \
-            "HEAD:${DOCS_BRANCH}"
+          for attempt in 1 2 3; do
+            if git push "https://x-access-token:${DOCS_TOKEN}`@github.com/ory/docs.git`" \
+              "HEAD:${DOCS_BRANCH}"; then
+              exit 0
+            fi
+
+            git fetch origin "$DOCS_BRANCH"
+            git rebase "origin/${DOCS_BRANCH}"
+          done
+
+          exit 1
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
git stash
git pull --rebase origin "$DOCS_BRANCH"
git stash apply
git add -- "$OUTPUT_DIR"
git commit -m "autogen(docs): generate cli docs"
echo "changed=true" >> "$GITHUB_OUTPUT"
- name: Push generated docs
if: ${{ steps.commit.outputs.changed == 'true' }}
working-directory: docs
env:
DOCS_BRANCH: ${{ inputs.docs-branch }}
DOCS_TOKEN: ${{ secrets.token }}
run: |
git push "https://x-access-token:${DOCS_TOKEN}@github.com/ory/docs.git" \
"HEAD:${DOCS_BRANCH}"
git stash
git pull --rebase origin "$DOCS_BRANCH"
git stash apply
git add -- "$OUTPUT_DIR"
git commit -m "autogen(docs): generate cli docs"
echo "changed=true" >> "$GITHUB_OUTPUT"
- name: Push generated docs
if: ${{ steps.commit.outputs.changed == 'true' }}
working-directory: docs
env:
DOCS_BRANCH: ${{ inputs.docs-branch }}
DOCS_TOKEN: ${{ secrets.token }}
run: |
for attempt in 1 2 3; do
if git push "https://x-access-token:${DOCS_TOKEN}`@github.com/ory/docs.git`" \
"HEAD:${DOCS_BRANCH}"; then
exit 0
fi
git fetch origin "$DOCS_BRANCH"
git rebase "origin/${DOCS_BRANCH}"
done
exit 1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docs-cli-next.yml around lines 171 - 185, Update the “Push
generated docs” step to handle a non-fast-forward push by rebasing the existing
local commit onto the latest DOCS_BRANCH and retrying the push. Keep the normal
push path unchanged, and abort safely without force-pushing if the rebase
encounters conflicts.

Comment thread .github/workflows/test.yml Outdated
Comment on lines +31 to +37
files=(
checkout/action.yml
releaser/action.yml
newsletter/action.yml
newsletter/slack-notify/action.yml
.github/workflows/docs-cli-next.yml
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Scan all workflow and composite-action manifests.

Line 31 uses a fixed file list. The check does not scan .github/workflows/labels.yml, which contains mutable actions/checkout@v2 and ory/label-sync-action@v0 references. The immutable-dependency check can pass while mutable references remain in the repository.

Discover all workflow YAML files and action.yml or action.yaml manifests before running the validation.

Proposed fix
-          files=(
-            checkout/action.yml
-            releaser/action.yml
-            newsletter/action.yml
-            newsletter/slack-notify/action.yml
-            .github/workflows/docs-cli-next.yml
-          )
+          mapfile -d '' -t files < <(
+            find .github/workflows -type f \
+              \( -name '*.yml' -o -name '*.yaml' \) -print0
+            find . -path ./.git -prune -o -type f \
+              \( -name 'action.yml' -o -name 'action.yaml' \) -print0
+          )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
files=(
checkout/action.yml
releaser/action.yml
newsletter/action.yml
newsletter/slack-notify/action.yml
.github/workflows/docs-cli-next.yml
)
mapfile -d '' -t files < <(
find .github/workflows -type f \
\( -name '*.yml' -o -name '*.yaml' \) -print0
find . -path ./.git -prune -o -type f \
\( -name 'action.yml' -o -name 'action.yaml' \) -print0
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test.yml around lines 31 - 37, Replace the hard-coded
files array in the workflow validation step with discovery of all YAML workflow
files under .github/workflows and all action.yml/action.yaml manifests,
including labels.yml. Pass the complete discovered file set to the
immutable-dependency check so every workflow and composite-action manifest is
scanned.

Comment thread newsletter/action.yml Outdated
Comment on lines +20 to +28
required: false

runs:
using: "composite"
steps:
- uses: ory/ci/checkout@master
- run: |
git fetch origin +refs/tags/*:refs/tags/*
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0
persist-credentials: false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 12 \
  'newsletter|ssh_key:|mailchimp_api_key:|permissions:|contents:' \
  --glob '*.yml' --glob '*.yaml' .

Repository: ory/ci

Length of output: 9083


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- newsletter action references ---'
rg -n -C 10 \
  '(uses:.*newsletter|ory/ci/.*/newsletter|newsletter/action\.yml|ssh_key:)' \
  --glob '*.yml' --glob '*.yaml' --glob '*.yaml.tmpl' --glob '*.yml.tmpl' .

printf '%s\n' '--- workflow permission blocks ---'
rg -n -C 8 \
  '(^|[[:space:]])permissions:|contents:' \
  --glob '*.yml' --glob '*.yaml' --glob '*.yaml.tmpl' --glob '*.yml.tmpl' . || true

printf '%s\n' '--- all tracked workflow/action files ---'
git ls-files | rg '(^|/)(\.github/workflows|action\.yml$|action\.yaml$)'

Repository: ory/ci

Length of output: 2099


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import pathlib, re

tracked = pathlib.Path(".git").joinpath("index")
files = []
import subprocess
files = subprocess.check_output(["git", "ls-files", "-z"]).decode().split("\0")

newsletter_refs = []
permission_refs = []
for name in files:
    if not name:
        continue
    try:
        text = pathlib.Path(name).read_text(errors="replace")
    except OSError:
        continue
    if re.search(r"newsletter", text, re.I):
        for number, line in enumerate(text.splitlines(), 1):
            if re.search(r"newsletter", line, re.I):
                newsletter_refs.append((name, number, line.strip()))
    if re.search(r"(^|\s)permissions:|contents:", text):
        for number, line in enumerate(text.splitlines(), 1):
            if re.search(r"(^|\s)permissions:|contents:", line):
                permission_refs.append((name, number, line.strip()))

print("NEWSLETTER_REFERENCES")
for item in newsletter_refs:
    print(f"{item[0]}:{item[1]}:{item[2]}")
print("PERMISSION_REFERENCES")
for item in permission_refs:
    print(f"{item[0]}:{item[1]}:{item[2]}")
PY

printf '%s\n' 'NEWSLETTER_ACTION'
cat -n newsletter/action.yml

Repository: ory/ci

Length of output: 5242


🌐 Web query:

GitHub Actions actions/checkout token input default GITHUB_TOKEN contents read permission documentation

💡 Result:

The actions/checkout action uses the GITHUB_TOKEN by default (via the token input) to fetch the repository [1][2]. To ensure proper functionality, it is recommended to explicitly set the contents permission to read [1][3]. When using the actions/checkout action, you should configure your workflow or job permissions as follows: permissions: contents: read If you do not explicitly define a permissions block, the GITHUB_TOKEN permissions depend on your repository or organization settings [4]. Following the principle of least privilege by setting contents: read ensures that the checkout action has the minimum access required to clone your repository [1][4][5].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- .github/workflows/test.yml ---'
cat -n .github/workflows/test.yml | sed -n '1,60p'

printf '%s\n' '--- README newsletter usage ---'
cat -n README.md | sed -n '28,58p'

printf '%s\n' '--- newsletter action call sites ---'
rg -n -C 8 \
  'uses:.*(newsletter|ory/ci)' \
  --glob '*.yml' --glob '*.yaml' --glob 'README*' .

Repository: ory/ci

Length of output: 17713


Document the required caller permission.

The in-repository caller grants contents: read, but the README example does not specify this requirement. External callers without repository read access can fail during checkout before ssh_key is configured. Add permissions: contents: read to the usage documentation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@newsletter/action.yml` around lines 20 - 28, Update the README usage example
for the newsletter action to declare the required caller permission, adding
contents: read under permissions. Keep the existing example configuration and
checkout behavior unchanged.

Comment thread newsletter/action.yml Outdated
Comment on lines +39 to +41
curl --fail --silent --show-error --location --retry 7 \
--output "${workdir}/${archive}" \
"https://github.com/ory/cli/releases/download/v${version}/${archive}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

file="newsletter/action.yml"
printf '%s\n' '--- relevant action definition ---'
sed -n '1,90p' "$file"

printf '%s\n' '--- curl invocations and timeout settings ---'
rg -n -C 3 'curl|connect-timeout|max-time|retry-max-time|retry' -- "$file" newsletter .github 2>/dev/null || true

printf '%s\n' '--- curl version and option behavior ---'
curl --version | head -n 2
curl --help all | rg -n -- '--connect-timeout|--max-time|--retry-max-time|--retry'

Repository: ory/ci

Length of output: 5373


🌐 Web query:

site:curl.se/docs/manpage.html curl --retry --connect-timeout --max-time --retry-max-time semantics

💡 Result:

In curl, the following command-line options control connection behavior, execution duration, and retry logic: --connect-timeout This option specifies the maximum time in seconds allowed for the connection phase [1]. This includes DNS lookup, TCP, TLS, or QUIC handshakes [1]. If the connection is not established within this time, curl will exit [1]. --max-time This option sets the maximum time in seconds that the entire transfer operation is allowed to take. If the operation exceeds this limit, curl will terminate the transfer. --retry This option defines the number of times curl should retry a failed transfer if the failure is considered transient (e.g., connection-related errors or certain HTTP server errors). If set to 0, it will not retry. --retry-max-time This option sets an absolute time limit in seconds for the entire retry process. Regardless of how many retries remain, curl will stop retrying once this total time has elapsed since the start of the first attempt. These options can be combined to manage how curl handles unreliable networks or unresponsive servers, ensuring that individual connections fail fast while allowing for a total duration across multiple attempts [1].

Citations:


Bound the Ory download time.

Add --connect-timeout 15 --max-time 120 --retry-max-time 300. --retry 7 limits retry count but does not limit connection, transfer, or total retry time.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@newsletter/action.yml` around lines 39 - 41, Add the requested curl timeout
options to the download command in the newsletter action: use a 15-second
connection timeout, 120-second maximum transfer time, and 300-second maximum
retry duration, while preserving the existing retry count and other flags.

Comment thread newsletter/action.yml
Comment on lines 49 to 58
SSH_AUTH_SOCK: /tmp/ssh_agent.sock
run: |
set -euo pipefail
mkdir -p /home/runner/.ssh
ssh-keyscan github.com >> /home/runner/.ssh/known_hosts
echo "${{ inputs.ssh_key }}" > /home/runner/.ssh/github_actions
echo "${SSH_KEY}" > /home/runner/.ssh/github_actions
chmod 600 /home/runner/.ssh/github_actions
ssh-agent -a $SSH_AUTH_SOCK > /dev/null
ssh-agent -a "$SSH_AUTH_SOCK" > /dev/null
ssh-add /home/runner/.ssh/github_actions
git config --global core.sshCommand "ssh -i /home/runner/.ssh/github_actions"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- newsletter/action.yml ---'
nl -ba newsletter/action.yml | sed -n '1,140p'

printf '%s\n' '--- SSH-related references ---'
rg -n -C 3 'SSH_AUTH_SOCK|github_actions|core\.sshCommand|ssh-agent|ssh-add|ssh-keyscan|SSH_KEY' . \
  -g '!node_modules' -g '!dist' -g '!build'

Repository: ory/ci

Length of output: 214


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- newsletter/action.yml ---'
awk '{printf "%6d\t%s\n", NR, $0}' newsletter/action.yml | sed -n '1,140p'

printf '%s\n' '--- SSH-related references ---'
rg -n -C 3 'SSH_AUTH_SOCK|github_actions|core\.sshCommand|ssh-agent|ssh-add|ssh-keyscan|SSH_KEY' . \
  -g '!node_modules' -g '!dist' -g '!build' || true

Repository: ory/ci

Length of output: 5304


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- newsletter callers ---'
rg -n -C 5 'newsletter/action\.yml|uses: .*newsletter|ssh_key:' . \
  -g '*.yml' -g '*.yaml' || true

printf '%s\n' '--- newsletter implementation references ---'
rg -n -C 4 'release notify|notify send|notify draft|core\.sshCommand|git fetch|git clone' . \
  -g '!node_modules' -g '!dist' -g '!build' || true

printf '%s\n' '--- cleanup and Git configuration references ---'
rg -n -C 3 'git config --global|git config --unset|ssh-agent -k|SSH_AUTH_SOCK|RUNNER_TEMP|known_hosts|github_actions' . \
  -g '!node_modules' -g '!dist' -g '!build' || true

Repository: ory/ci

Length of output: 21086


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

path = Path("newsletter/action.yml")
text = path.read_text()
lines = text.splitlines()

checks = {
    "private key is written": re.search(
        r'echo "\$\{SSH_KEY\}" > (/[^ \n]+)', text
    ),
    "fixed SSH agent socket is configured": re.search(
        r'SSH_AUTH_SOCK:\s*(\S+)', text
    ),
    "agent is started": "ssh-agent -a \"$SSH_AUTH_SOCK\"" in text,
    "key is added to agent": "ssh-add /home/runner/.ssh/github_actions" in text,
    "global SSH command is configured": (
        'git config --global core.sshCommand "ssh -i /home/runner/.ssh/github_actions"'
        in text
    ),
    "cleanup command exists": any(
        re.search(r'\b(rm|shred|unlink|git config --unset|ssh-agent -k|kill)\b', line)
        for line in lines
    ),
    "SSH_AUTH_SOCK is exported for the newsletter step": bool(
        re.search(
            r'- name: Draft or send the newsletter(?:(?!\n    - ).)*'
            r'SSH_AUTH_SOCK:',
            text,
            re.S,
        )
    ),
}

for name, result in checks.items():
    print(f"{name}: {'yes' if result else 'no'}")
if checks["private key is written"]:
    print(f"key path: {checks['private key is written'].group(1)}")
if checks["fixed SSH agent socket is configured"]:
    print(f"socket path: {checks['fixed SSH agent socket is configured'].group(1)}")
PY

Repository: ory/ci

Length of output: 459


🌐 Web query:

GitHub Actions documentation composite action steps environment variables scope GITHUB_ENV

💡 Result:

In GitHub Actions, using the GITHUB_ENV environment file within a composite action allows you to define or update environment variables for subsequent steps [1]. Scope and Behavior: 1. Persistence: When you write a variable to $GITHUB_ENV inside a step of a composite action, that variable becomes available to subsequent steps in the same job, including steps that occur after the composite action has finished executing [1]. 2. Availability within the Composite Action: While GITHUB_ENV is designed to export variables for future steps, it does not make the variable immediately available in the current step where the command is executed [1]. To use the variable in later steps within the same composite action, you can reference it using the env context (e.g., ${{ env.VAR_NAME }}) or directly as an environment variable in shell commands (e.g., $VAR_NAME) [2][3]. 3. Scope: Variables set via GITHUB_ENV are scoped to the entire workflow job [2]. They are not restricted to the composite action itself; any step in the workflow job that runs after the step that set the variable will have access to it [1]. Best Practices and Considerations: - If you need an environment variable to be available only within specific steps of your composite action, you can define it using the env key at the step level within the composite action's metadata [2][4]. - Issues regarding the immediate availability of variables updated within a composite action have been historically reported [5]. The standard pattern for ensuring availability is to export the variable to GITHUB_ENV and then access it in subsequent steps [6][1][3]. - Due to security restrictions, GITHUB_ENV cannot be used to set certain environment variables, such as NODE_OPTIONS [1].

Citations:


Clean up the SSH credential before the composite action exits.

Use unique paths under $RUNNER_TEMP, avoid git config --global, and remove the key and temporary SSH files in an if: ${{ always() }} step. The agent is not available to the newsletter step because SSH_AUTH_SOCK is step-scoped, and the configured core.sshCommand reads the key directly. Do not start the agent unless its socket and PID are passed to the required steps and the agent is stopped during cleanup.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@newsletter/action.yml` around lines 49 - 58, Update the SSH setup run block
to store the key, known_hosts, and related files under unique paths in
$RUNNER_TEMP, and configure core.sshCommand locally rather than globally. Remove
ssh-agent startup and ssh-add unless the socket and PID are explicitly passed to
all required steps. Add a separate cleanup step with if: ${{ always() }} that
removes the temporary key and SSH files and stops any started agent.

Comment thread newsletter/action.yml
Comment on lines 53 to 58
ssh-keyscan github.com >> /home/runner/.ssh/known_hosts
echo "${{ inputs.ssh_key }}" > /home/runner/.ssh/github_actions
echo "${SSH_KEY}" > /home/runner/.ssh/github_actions
chmod 600 /home/runner/.ssh/github_actions
ssh-agent -a $SSH_AUTH_SOCK > /dev/null
ssh-agent -a "$SSH_AUTH_SOCK" > /dev/null
ssh-add /home/runner/.ssh/github_actions
git config --global core.sshCommand "ssh -i /home/runner/.ssh/github_actions"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- action files ---'
git ls-files '*action.yml' '*action.yaml' | sed -n '1,120p'

printf '%s\n' '--- newsletter/action.yml context ---'
nl -ba newsletter/action.yml | sed -n '1,110p'

printf '%s\n' '--- SSH-related configuration and callers ---'
rg -n -C 3 'ssh-keyscan|known_hosts|StrictHostKeyChecking|UserKnownHostsFile|github_actions|ssh-key|newsletter' .github newsletter --glob '!**/node_modules/**' --glob '!**/dist/**' | sed -n '1,260p'

Repository: ory/ci

Length of output: 4753


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- newsletter/action.yml ---'
sed -n '1,110p' newsletter/action.yml

printf '%s\n' '--- workflow references to the newsletter action ---'
rg -n -C 8 'uses:.*newsletter|newsletter/action|contents:|permissions:' .github --glob '*.yml' --glob '*.yaml' | sed -n '1,320p'

printf '%s\n' '--- checkout ordering in the action ---'
rg -n -C 12 'actions/checkout|uses:|ssh-key|working-directory|git ' newsletter/action.yml

Repository: ory/ci

Length of output: 10036


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- effective OpenSSH defaults for github.com ---'
ssh -G -o BatchMode=yes github.com 2>/dev/null |
  awk '$1=="stricthostkeychecking" || $1=="userknownhostsfile" || $1=="globalknownhostsfile" || $1=="identityfile" { print }'

printf '%s\n' '--- action pin check implementation ---'
sed -n '27,75p' .github/workflows/test.yml

printf '%s\n' '--- repository-wide newsletter action references ---'
rg -n -C 5 'uses:[[:space:]]+[^#]*newsletter|newsletter/action\.yml|ssh_key:[[:space:]]*' . --glob '!**/.git/**' --glob '!**/node_modules/**' --glob '!**/dist/**' | sed -n '1,240p'

Repository: ory/ci

Length of output: 3125


Pin GitHub’s SSH host key before configuring Git.

ssh-keyscan github.com trusts the key returned by the current network path. An attacker controlling that path can add an impostor key before later Git operations. Use GitHub’s published host key or compare the scan with a pinned fingerprint, then set StrictHostKeyChecking=yes and a job-local UserKnownHostsFile in core.sshCommand.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@newsletter/action.yml` around lines 53 - 58, Update the SSH setup around
ssh-keyscan and the global core.sshCommand configuration to validate github.com
against GitHub’s published host key or a pinned fingerprint before writing
known_hosts. Configure core.sshCommand with StrictHostKeyChecking=yes and a
job-local UserKnownHostsFile, while preserving the existing key and ssh-agent
setup.

Comment thread releaser/action.yml Outdated
Comment on lines +127 to +135
git stash || true
git checkout master || true
git stash pop || true
git commit -a -m "autogen: update release artifacts" -m "[skip ci]" || true
git pull origin master --rebase || true
git pull "https://x-access-token:${ORY_BOT_PAT}@github.com/${GITHUB_REPOSITORY}.git" \
master --rebase || true
if [[ "$GITHUB_REF_TYPE" == "branch" ]]; then
git push origin "${GITHUB_HEAD_REF:-$GITHUB_REF_NAME}" || true
git push "https://x-access-token:${ORY_BOT_PAT}@github.com/${GITHUB_REPOSITORY}.git" \
"${GITHUB_HEAD_REF:-$GITHUB_REF_NAME}" || true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Do not hide Git failures in publication steps.

|| true lets conflicts, failed checkouts, failed rebases, and failed pushes report a successful release. The post-release path can also continue after a failed git checkout master.

  • releaser/action.yml#L127-L135: let checkout, stash restoration, pull, and push failures stop the action; handle only an expected no-change commit explicitly.
  • releaser/action.yml#L163-L170: apply the same failure handling before pushing HEAD:master.
📍 Affects 1 file
  • releaser/action.yml#L127-L135 (this comment)
  • releaser/action.yml#L163-L170
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@releaser/action.yml` around lines 127 - 135, Remove blanket “|| true”
suppression from the publication Git commands in releaser/action.yml lines
127-135, allowing stash restoration, checkout master, pull/rebase, and push
failures to stop the action; handle only the expected no-change commit result
explicitly. Apply the same failure-handling correction to the push flow at
releaser/action.yml lines 163-170 before pushing HEAD:master.

Comment thread releaser/action.yml Outdated
Comment on lines +149 to +155
if [[ $(git describe --tags) == *"pre"* ]]; then
echo "This is a pre-release, skipping docs publishing."
exit 0
fi

make post-release

echo "publish=true" >> "$GITHUB_OUTPUT"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Check the triggering release ref for pre-releases.

Lines 128 and 164 switch the worktree to master. Line 149 can then inspect a tag reachable from master, not ${GITHUB_REF_NAME}. A pre-release can publish documentation when master resolves to a non-pre-release tag.

Use the triggering ref for this check.

Proposed fix
-        if [[ $(git describe --tags) == *"pre"* ]]; then
+        if [[ "$GITHUB_REF_NAME" == *"pre"* ]]; then
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [[ $(git describe --tags) == *"pre"* ]]; then
echo "This is a pre-release, skipping docs publishing."
exit 0
fi
make post-release
echo "publish=true" >> "$GITHUB_OUTPUT"
if [[ "$GITHUB_REF_NAME" == *"pre"* ]]; then
echo "This is a pre-release, skipping docs publishing."
exit 0
fi
make post-release
echo "publish=true" >> "$GITHUB_OUTPUT"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@releaser/action.yml` around lines 149 - 155, Update the pre-release check in
the release workflow to inspect the triggering release ref `${GITHUB_REF_NAME}`
rather than relying on `git describe --tags` from the worktree switched to
master. Preserve the existing skip message and exit behavior for pre-release
refs, while allowing documentation publishing for regular releases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant