Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 50 additions & 14 deletions .github/actions/cora-review/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ runs:
using: 'composite'
steps:
- name: Fetch LLM secrets from Infisical
uses: Infisical/secrets-action@v1.0.9
uses: Infisical/secrets-action@8a06c1bdcd5b8635d510c52d4b57a92c1ccef785 # v1.0.9
with:
method: 'oidc'
identity-id: ${{ inputs.infisical-identity-id }}
Expand All @@ -52,9 +52,10 @@ runs:
- name: Resolve cora-cli version
id: resolve
shell: bash
env:
INPUT_VERSION: ${{ inputs.cora-version }}
run: |
if [ "${{ inputs.cora-version }}" = "latest" ]; then
# Try up to 3 times with delay
if [ "$INPUT_VERSION" = "latest" ]; then
for i in 1 2 3; do
VERSION=$(curl -sfL --connect-timeout 10 \
https://api.github.com/repos/ajianaz/cora-cli/releases/latest \
Expand All @@ -65,19 +66,20 @@ runs:
echo "Attempt $i failed, retrying..."
sleep 5
done
# Fallback to a known good version if all retries fail
if [ -z "$VERSION" ]; then
echo "::warning::Failed to resolve latest cora-cli version from GitHub API, using fallback"
VERSION="v0.1.6"
VERSION="v0.1.7"
fi
else
VERSION="${{ inputs.cora-version }}"
VERSION="$INPUT_VERSION"
fi
echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT"
echo "Resolved cora-cli version: $VERSION"

- name: Install cora-cli
shell: bash
env:
CORA_VERSION: ${{ steps.resolve.outputs.VERSION }}
run: |
ARCH=$(uname -m)
if [ "$ARCH" = "x86_64" ]; then
Expand All @@ -88,7 +90,27 @@ runs:
echo "Unsupported architecture: $ARCH"
exit 1
fi
curl -sL "https://github.com/ajianaz/cora-cli/releases/download/${{ steps.resolve.outputs.VERSION }}/${ASSET}-${{ steps.resolve.outputs.VERSION }}.tar.gz" | tar xz -C /usr/local/bin cora
curl -sL "https://github.com/ajianaz/cora-cli/releases/download/${CORA_VERSION}/${ASSET}-${CORA_VERSION}.tar.gz" \
-o /tmp/cora.tar.gz
curl -sL "https://github.com/ajianaz/cora-cli/releases/download/${CORA_VERSION}/checksums-sha256.txt" \
-o /tmp/cora-checksums.txt || true
if [ -f /tmp/cora-checksums.txt ] && [ -s /tmp/cora-checksums.txt ]; then
EXPECTED=$(grep "${ASSET}-${CORA_VERSION}.tar.gz" /tmp/cora-checksums.txt | awk '{print $1}' || true)
ACTUAL=$(sha256sum /tmp/cora.tar.gz | awk '{print $1}')
if [ -n "$EXPECTED" ] && [ "$EXPECTED" != "$ACTUAL" ]; then
echo "::error::Checksum verification failed for ${ASSET}-${CORA_VERSION}.tar.gz"
echo "Expected: $EXPECTED"
echo "Actual: $ACTUAL"
exit 1
elif [ -z "$EXPECTED" ]; then
echo "::warning::No checksum found for ${ASSET}-${CORA_VERSION}.tar.gz in checksums file"
fi
rm -f /tmp/cora-checksums.txt
else
echo "::warning::No checksums file found, skipping verification"
Comment on lines +93 to +110

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
ls -la .github/actions/cora-review || true
sed -n '1,200p' .github/actions/cora-review/action.yml

Repository: ajianaz/cora-cli

Length of output: 8098


🏁 Script executed:

rg -n "curl -sL|checksums-sha256\.txt|tar xzf /tmp/cora\.tar\.gz" -S .github/actions/cora-review/action.yml

Repository: ajianaz/cora-cli

Length of output: 361


🏁 Script executed:

rg -n "curl -sL|checksums-sha256\.txt|tar xzf /tmp/cora\.tar\.gz" .github/actions/cora-review/action.yml

Repository: ajianaz/cora-cli

Length of output: 361


🏁 Script executed:

rg -n "curl -sL|checksums-sha256\.txt|tar xzf /tmp/cora\.tar\.gz" .github/actions/cora-review/action.yml

Repository: ajianaz/cora-cli

Length of output: 361


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "curl -sL|checksums-sha256\.txt|tar xzf /tmp/cora\.tar\.gz" .github/actions/cora-review/action.yml

Repository: ajianaz/cora-cli

Length of output: 361


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "curl -sL|checksums-sha256\.txt|tar xzf /tmp/cora\.tar\.gz" .github/actions/cora-review/action.yml

Repository: ajianaz/cora-cli

Length of output: 361


Fail cora asset downloads on HTTP errors (current curl -sL can silently write error pages).

.github/actions/cora-review/action.yml uses curl -sL to download both the tarball and checksums-sha256.txt. If GitHub returns 404/500, curl can still create /tmp/cora.tar.gz and /tmp/cora-checksums.txt; because verification only errors when EXPECTED is non-empty, a bogus/empty EXPECTED can skip checksum validation and the workflow may fail later during tar.

Suggested fix
-        curl -sL "https://github.com/ajianaz/cora-cli/releases/download/${CORA_VERSION}/${ASSET}-${CORA_VERSION}.tar.gz" \
+        curl -sfL --retry 3 --retry-delay 5 "https://github.com/ajianaz/cora-cli/releases/download/${CORA_VERSION}/${ASSET}-${CORA_VERSION}.tar.gz" \
           -o /tmp/cora.tar.gz
-        curl -sL "https://github.com/ajianaz/cora-cli/releases/download/${CORA_VERSION}/checksums-sha256.txt" \
-          -o /tmp/cora-checksums.txt || true
-        if [ -f /tmp/cora-checksums.txt ]; then
+        if curl -sfL --retry 3 --retry-delay 5 "https://github.com/ajianaz/cora-cli/releases/download/${CORA_VERSION}/checksums-sha256.txt" \
+          -o /tmp/cora-checksums.txt; then
           EXPECTED=$(grep "${ASSET}-${CORA_VERSION}.tar.gz" /tmp/cora-checksums.txt | awk '{print $1}')
+          if [ -z "$EXPECTED" ]; then
+            echo "::error::Missing checksum entry for ${ASSET}-${CORA_VERSION}.tar.gz"
+            exit 1
+          fi
           ACTUAL=$(sha256sum /tmp/cora.tar.gz | awk '{print $1}')
           if [ -n "$EXPECTED" ] && [ "$EXPECTED" != "$ACTUAL" ]; then
🤖 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/actions/cora-review/action.yml around lines 93 - 108, The current
download step can silently produce HTML error pages because curl is run with
-sL; change the download logic to make HTTP errors fail and verify the files are
real: use curl with fail+show-error (e.g., -fSLo) when fetching the tarball and
checksums, check curl's exit status after each download, and ensure
/tmp/cora.tar.gz and /tmp/cora-checksums.txt exist and are non-empty before
proceeding; if the checksums download failed (empty or curl non-zero) treat it
as an error instead of emitting a warning, and continue to compute
EXPECTED/ACTUAL only when the checksum file is valid (use CORA_VERSION, ASSET,
/tmp/cora.tar.gz and /tmp/cora-checksums.txt to locate the relevant checks).

fi
tar xzf /tmp/cora.tar.gz -C /usr/local/bin cora
rm -f /tmp/cora.tar.gz
chmod +x /usr/local/bin/cora
cora --version

Expand All @@ -99,26 +121,31 @@ runs:
CORA_API_KEY: ${{ env.CORA_API_KEY }}
CORA_BASE_URL: ${{ env.CORA_BASE_URL }}
CORA_MODEL: ${{ env.CORA_MODEL }}
INPUT_BASE: ${{ inputs.base-branch }}
INPUT_SEVERITY: ${{ inputs.severity }}
run: |
cora review \
--base ${{ inputs.base-branch }} \
--base "$INPUT_BASE" \
--format sarif \
--severity ${{ inputs.severity }} \
--severity "$INPUT_SEVERITY" \
--quiet \
> cora-results.sarif 2>/dev/null
> cora-results.sarif 2>cora-stderr.log || true
echo "Cora review complete ($(wc -c < cora-results.sarif) bytes)"
if [ -s cora-stderr.log ]; then
echo "::warning::Cora stderr: $(cat cora-stderr.log)"
fi
Comment on lines +134 to +136

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/actions/cora-review/action.yml"
echo "== File: $FILE =="
if [ ! -f "$FILE" ]; then
  echo "MISSING: $FILE"
  exit 1
fi

# Show line range around 120-150
nl -ba "$FILE" | sed -n '110,170p'

# Search for cora-stderr usage
echo "== rg cora-stderr.log =="
rg -n "cora-stderr\.log" -S .github/actions/cora-review/action.yml .github/actions || true

Repository: ajianaz/cora-cli

Length of output: 173


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/actions/cora-review/action.yml"
echo "== File: $FILE =="
if [ ! -f "$FILE" ]; then
  echo "MISSING: $FILE"
  exit 1
fi

# Show line range around 120-170 (covers 132-134)
nl -ba "$FILE" | sed -n '110,170p'

echo "== rg cora-stderr.log (broader context) =="
rg -n "cora-stderr\.log" -S .github/actions || true

echo "== rg for '::warning::Cora stderr' =="
rg -n "::warning::Cora stderr" -S .github/actions || true

Repository: ajianaz/cora-cli

Length of output: 173


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/actions/cora-review/action.yml"
echo "== File: $FILE =="
if [ ! -f "$FILE" ]; then
  echo "MISSING: $FILE"
  exit 1
fi

echo "== Lines 110-170 =="
cat -n "$FILE" | sed -n '110,170p'

echo "== rg cora-stderr.log (action dir) =="
rg -n "cora-stderr\.log" -S .github/actions || true

echo "== rg ::warning::Cora stderr =="
rg -n "::warning::Cora stderr" -S .github/actions || true

Repository: ajianaz/cora-cli

Length of output: 3255


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/actions/cora-review/action.yml"
echo "== File: $FILE =="
if [ ! -f "$FILE" ]; then
  echo "MISSING: $FILE"
  exit 1
fi

echo "== Lines 110-170 =="
cat -n "$FILE" | sed -n '110,170p'

echo "== rg cora-stderr.log (action dir) =="
rg -n "cora-stderr\.log" -S .github/actions || true

echo "== rg ::warning::Cora stderr =="
rg -n "::warning::Cora stderr" -S .github/actions || true

Repository: ajianaz/cora-cli

Length of output: 3255


🌐 Web query:

GitHub Actions workflow command ::stop-commands::and matching:::: syntax how it works

💡 Result:

The GitHub Actions workflow command::stop-commands:: is used to disable the processing of subsequent workflow commands, preventing the runner from interpreting them as instructions [1]. The is a unique, randomly generated token that acts as a key to both stop and resume command processing [1][2]. How it works: 1. Disabling commands: When you execute echo "::stop-commands::", the runner stops parsing any following output for workflow commands [1]. This is useful when you need to print text to the logs that happens to contain patterns that look like GitHub Actions commands (e.g., scripts with comments) and you want to ensure they are not accidentally executed by the runner [1]. 2. Resuming commands: To re-enable command processing, you must output the exact same token using the syntax:::: [1]. This tells the runner to resume parsing the workflow output for commands [1]. Best Practices: - Unique tokens: Always use a randomly generated, unique token for each workflow run (e.g., using uuidgen or a language-specific random generator) to ensure security and prevent accidental re-enabling of commands [1]. - Masking: Because the token used to stop commands is echoed to the logs, it is recommended to mask the token using::add-mask:: if you want to keep the token itself hidden from the log output [3]. Example: # Generate a unique token stopMarker=$(uuidgen) # Mask the token for security echo "::add-mask::$stopMarker" # Disable command processing echo "::stop-commands::$stopMarker" # The following will NOT be processed as commands echo "::warning:: This will not be rendered as a warning" # Resume command processing echo "::$stopMarker::" # Commands are processed normally again echo "::warning:: This will be rendered as a warning"

Citations:


Don't emit raw cora-stderr.log inside a workflow command in .github/actions/cora-review/action.yml (lines 132-134): echo "::warning::Cora stderr: $(cat cora-stderr.log)" prints untrusted stderr directly in workflow-command format, so any ::...::... content coming from cora-stderr.log can be parsed as additional workflow commands by the runner.

Suggested fix
         if [ -s cora-stderr.log ]; then
-          echo "::warning::Cora stderr: $(cat cora-stderr.log)"
+          stop_marker=$(python3 - <<'PY'
+import uuid
+print(uuid.uuid4())
+PY
+)
+          echo "::warning::Cora emitted stderr; see grouped log output below."
+          echo "::group::Cora stderr"
+          echo "::stop-commands::$stop_marker"
+          cat cora-stderr.log
+          echo "::$stop_marker::"
+          echo "::endgroup::"
         fi
📝 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 [ -s cora-stderr.log ]; then
echo "::warning::Cora stderr: $(cat cora-stderr.log)"
fi
if [ -s cora-stderr.log ]; then
stop_marker=$(python3 - <<'PY'
import uuid
print(uuid.uuid4())
PY
)
echo "::warning::Cora emitted stderr; see grouped log output below."
echo "::group::Cora stderr"
echo "::stop-commands::$stop_marker"
cat cora-stderr.log
echo "::$stop_marker::"
echo "::endgroup::"
fi
🤖 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/actions/cora-review/action.yml around lines 132 - 134, The workflow
currently emits raw cora-stderr.log via the command echo "::warning::Cora
stderr: $(cat cora-stderr.log)", which can be interpreted as additional workflow
commands; replace that emission with a safe approach: either upload
cora-stderr.log as an artifact or sanitize/escape the file contents before
sending them to the workflow-command annotation. Specifically, update the block
that checks cora-stderr.log (the if that contains echo "::warning::Cora stderr:
$(cat cora-stderr.log)") to avoid injecting raw file contents into a workflow
command—escape percent/newline/carriage-return and any literal "::" sequences
when constructing the ::warning:: annotation or switch to artifact upload so the
raw stderr is not parsed by the runner.


- name: Upload SARIF to GitHub Code Scanning
if: inputs.upload-sarif == 'true'
continue-on-error: true
uses: github/codeql-action/upload-sarif@v4
uses: github/codeql-action/upload-sarif@f52b05f4acaaa234e44466e66d29050e135ea9ef # v4.36.0
with:
sarif_file: cora-results.sarif
category: cora-review

- name: Post PR comment
if: always() && github.event_name == 'pull_request'
uses: actions/github-script@v7
uses: actions/github-script@d746ffe35508b1917358783b479e04febd2b8f71 # v9.0.0
env:
GH_TOKEN: ${{ inputs.github-token }}
with:
Expand All @@ -134,7 +161,17 @@ runs:

let body;
if (!sarifContent || !sarifContent.runs || sarifContent.runs.length === 0) {
body = `## 🔍 Cora AI Code Review\n\n✅ **No issues found.** Code looks good!\n\n---\n_Review powered by [cora-cli](https://github.com/ajianaz/cora-cli) · BYOK · MIT_`;
let fileSize = 0;
try {
fileSize = fs.statSync('cora-results.sarif').size;
} catch (e) {
fileSize = 0;
}
if (fileSize < 10) {
body = `## 🔍 Cora AI Code Review\n\n⚠️ **Review could not complete.** Cora produced an empty result. Check the workflow logs for errors.\n\n---\n_Review powered by [cora-cli](https://github.com/ajianaz/cora-cli) · BYOK · MIT_`;
} else {
body = `## 🔍 Cora AI Code Review\n\n✅ **No issues found.** Code looks good!\n\n---\n_Review powered by [cora-cli](https://github.com/ajianaz/cora-cli) · BYOK · MIT_`;
}
Comment on lines +164 to +174

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Malformed non-empty SARIF is reported as a clean pass.

If parsing fails but cora-results.sarif is larger than 10 bytes, this branch posts “No issues found.” A truncated or otherwise invalid SARIF file should still be treated as a failed review, otherwise scanner failures surface as a false green.

Suggested fix
-          let sarifContent;
+          let sarifContent;
+          let sarifParseFailed = false;
           try {
             sarifContent = JSON.parse(fs.readFileSync('cora-results.sarif', 'utf8'));
           } catch (e) {
             sarifContent = null;
+            sarifParseFailed = true;
           }
@@
-          if (!sarifContent || !sarifContent.runs || sarifContent.runs.length === 0) {
+          if (!sarifContent || !sarifContent.runs || sarifContent.runs.length === 0) {
             let fileSize = 0;
             try {
               fileSize = fs.statSync('cora-results.sarif').size;
             } catch (e) {
               fileSize = 0;
             }
-            if (fileSize < 10) {
+            if (sarifParseFailed || fileSize < 10) {
               body = `## 🔍 Cora AI Code Review\n\n⚠️ **Review could not complete.** Cora produced an empty result. Check the workflow logs for errors.\n\n---\n_Review powered by [cora-cli](https://github.com/ajianaz/cora-cli) · BYOK · MIT_`;
             } else {
               body = `## 🔍 Cora AI Code Review\n\n✅ **No issues found.** Code looks good!\n\n---\n_Review powered by [cora-cli](https://github.com/ajianaz/cora-cli) · BYOK · MIT_`;
             }
🤖 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/actions/cora-review/action.yml around lines 162 - 172, The current
logic only checks fileSize and sets body to a clean pass when cora-results.sarif
> 10 bytes; instead, read and attempt to parse the SARIF content
(fs.readFileSync('cora-results.sarif','utf8') and JSON.parse) and if JSON.parse
throws or the parsed object lacks expected SARIF structure (e.g., no "runs"
array or malformed "runs[*].results") then set body to the failure message (same
as when fileSize < 10); update the branch that currently uses fileSize and the
body variable so parse errors or missing fields produce the "Review could not
complete" message rather than "No issues found."

} else {
const results = sarifContent.runs[0].results || [];
if (results.length === 0) {
Expand Down Expand Up @@ -173,7 +210,6 @@ runs:
}
}

// Find existing cora comment and update it, or create new
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
Expand Down
21 changes: 0 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,25 +65,4 @@ jobs:
- uses: Swatinem/rust-cache@v2
- run: cargo build --release

cora-review:
name: Cora Review
runs-on: ubuntu-latest
needs: [check, fmt, clippy, test]
if: github.event_name == 'pull_request'
permissions:
contents: read
security-events: write
pull-requests: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2

- uses: ./.github/actions/cora-review
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
infisical-identity-id: ${{ secrets.INFISICAL_IDENTITY_ID }}
34 changes: 34 additions & 0 deletions .github/workflows/cora-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Cora AI Code Review

on:
pull_request:
branches: [develop]
types: [opened, synchronize, ready_for_review, reopened]

concurrency:
group: cora-review-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read
pull-requests: write
id-token: write
security-events: write

jobs:
cora-review:
name: Cora Review
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false

- name: Run Cora AI Code Review
uses: ./.github/actions/cora-review
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
infisical-identity-id: ${{ secrets.INFISICAL_IDENTITY_ID }}
Loading