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
41 changes: 41 additions & 0 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: PR Tests & Coverage

on:
pull_request:
branches:
- main
Comment on lines +3 to +6

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Add a concurrency group.

Without a concurrency block, multiple pushes to the same PR queue redundant, overlapping test runs, wasting CI minutes and potentially producing out-of-order coverage comments.

♻️ Proposed addition
 on:
   pull_request:
     branches:
       - main
+
+concurrency:
+  group: pr-checks-${{ github.ref }}
+  cancel-in-progress: true
📝 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
on:
pull_request:
branches:
- main
on:
pull_request:
branches:
- main
concurrency:
group: pr-checks-${{ github.ref }}
cancel-in-progress: true
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 3-6: insufficient job-level concurrency limits (concurrency-limits): workflow is missing concurrency setting

(concurrency-limits)

🤖 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/pr-checks.yml around lines 3 - 6, The workflow trigger in
pr-checks.yml is missing a concurrency block, so repeated pushes to the same
pull request can start overlapping runs. Add a workflow-level concurrency group
tied to the pull request using the existing pull_request trigger context, and
enable cancel-in-progress so older runs are stopped when a new push arrives.
Keep the change alongside the current on: pull_request configuration so the
pr-checks workflow is deduplicated correctly.

Source: Linters/SAST tools


jobs:
test:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
Comment on lines +11 to +13

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does GitHub restrict GITHUB_TOKEN to read-only for pull_request workflows from forked repositories even when permissions: pull-requests: write is set?

💡 Result:

Yes, GitHub restricts the GITHUB_TOKEN to read-only permissions for workflows triggered by pull requests from forked repositories by default, regardless of any write permissions explicitly set in the workflow YAML file [1][2][3]. This restriction is a security measure designed to prevent untrusted code (which controls the workflow file in the pull request) from gaining write access to the base repository [3]. When a pull request originates from a fork, any write permissions defined in the workflow are automatically adjusted to read-only [1][2][4]. There are two primary ways to bypass this restriction: 1. Repository Settings: An administrator can modify the repository's GitHub Actions settings to enable the option "Send write tokens to workflows from pull requests" [1][2][4]. This allows write tokens to be sent to workflows triggered by forked pull requests [1]. 2. Use pull_request_target: If the workflow is changed to use the pull_request_target event instead of the pull_request event, it will run with the base repository's context and permissions [2][3]. Because pull_request_target workflows execute code from the base repository's default branch rather than the untrusted fork, they are granted a read/write GITHUB_TOKEN and access to secrets [3]. Note that using pull_request_target requires careful security practices to prevent malicious code injection [3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".github/workflows/pr-checks.yml"
echo "=== file exists ==="
test -f "$file" && echo yes

echo "=== relevant lines ==="
nl -ba "$file" | sed -n '1,120p'

echo "=== search for workflow trigger and comment step ==="
rg -n "pull_request|pull_request_target|MishaKav/pytest-coverage-comment|permissions:|pull-requests: write|contents: read" "$file"

Repository: StabilityNexus/MiniChain

Length of output: 249


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".github/workflows/pr-checks.yml"

echo "=== file ==="
sed -n '1,120p' "$file"

echo "=== matches ==="
rg -n "pull_request|pull_request_target|MishaKav/pytest-coverage-comment|permissions:|pull-requests: write|contents: read" "$file"

Repository: StabilityNexus/MiniChain

Length of output: 1331


Fork PRs won’t be able to post this comment
Lines 11-13, 38: pull-requests: write is reduced to read-only on pull_request runs from forks, so MishaKav/pytest-coverage-comment won’t update coverage comments for external contributors unless write tokens are enabled for fork PRs or the job moves to pull_request_target.

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 13-13: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment

(undocumented-permissions)

🤖 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/pr-checks.yml around lines 11 - 13, The coverage comment
step is running under a fork-safe pull_request context, so the GITHUB_TOKEN only
has read access and MishaKav/pytest-coverage-comment cannot update the PR.
Adjust the workflow in pr-checks.yml by changing the trigger/job context to one
that can write comments for external PRs, such as using pull_request_target with
appropriate safeguards or otherwise enabling write tokens for forked runs, and
verify the permissions for the coverage-comment job still allow pull-requests:
write when the action runs.


steps:
- name: Checkout Code
uses: actions/checkout@v4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Pin third-party/GitHub actions to a commit SHA.

zizmor flags actions/checkout@v4, actions/setup-python@v5, and MishaKav/pytest-coverage-comment@v1 as unpinned. Tags are mutable; pinning to a SHA prevents a compromised/retagged release from silently running in CI.

Also applies to: 22-22, 38-38

🧰 Tools
🪛 zizmor (1.26.1)

[error] 17-17: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 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/pr-checks.yml at line 17, Pin the third-party GitHub
Actions used in the workflow by replacing the mutable tags in the checkout,
setup-python, and pytest coverage comment steps with their corresponding commit
SHAs; update the steps referencing actions/checkout, actions/setup-python, and
MishaKav/pytest-coverage-comment so CI uses fixed revisions instead of version
tags.

Source: Linters/SAST tools

with:
fetch-depth: 0
Comment on lines +16 to +19

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Set persist-credentials: false on checkout.

This job never pushes, yet the default checkout leaves the GITHUB_TOKEN persisted in the local git config (flagged by zizmor as artipacked). The sibling workflow update-badge.yml already disables this at line 15 — apply the same here for consistency and reduced credential-exposure risk.

🔒️ Proposed fix
       - name: Checkout Code
         uses: actions/checkout@v4
         with:
+          persist-credentials: false
           fetch-depth: 0
📝 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
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Checkout Code
uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 0
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 16-19: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 17-17: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 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/pr-checks.yml around lines 16 - 19, The Checkout Code step
in the workflow should disable persisted git credentials because this job only
reads the repository and never pushes. Update the actions/checkout usage to
include persist-credentials set to false, matching the approach used in
update-badge.yml and keeping the checkout configuration consistent.

Source: Linters/SAST tools


- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"

- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-test.txt

- name: Run Tests with Coverage
run: |
pytest tests/ -v --junitxml=pytest.xml --cov-report=term-missing:skip-covered --cov=minichain | tee pytest-coverage.txt

- name: Pytest coverage comment
uses: MishaKav/pytest-coverage-comment@v1
with:
pytest-coverage-path: ./pytest-coverage.txt
junitxml-path: ./pytest.xml
50 changes: 50 additions & 0 deletions .github/workflows/update-badge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Update Coverage Badge
on:
push:
branches: [main]
Comment on lines +2 to +4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add a concurrency group.

Rapid successive pushes to main can trigger overlapping runs of this workflow; two concurrent git-auto-commit-action executions racing to commit/push README changes can produce conflicting commits or push failures.

♻️ Proposed addition
 on:
   push:
     branches: [main]
+
+concurrency:
+  group: update-badge-${{ github.ref }}
+  cancel-in-progress: false
📝 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
on:
push:
branches: [main]
on:
push:
branches: [main]
concurrency:
group: update-badge-${{ github.ref }}
cancel-in-progress: false
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 2-4: insufficient job-level concurrency limits (concurrency-limits): workflow is missing concurrency setting

(concurrency-limits)

🤖 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/update-badge.yml around lines 2 - 4, The update-badge
workflow can run multiple overlapping instances on rapid pushes to main, which
can cause git-auto-commit-action to race and fail. Add a workflow-level
concurrency setting to the update-badge job so only one run for the same branch
can execute at a time, and make sure it uses a stable group keyed by the
workflow name or ref with cancel-in-progress enabled.

Source: Linters/SAST tools


permissions:
contents: write
Comment on lines +6 to +7

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Scope contents: write to the job, not the workflow.

Workflow-level permissions apply to every job in the file; scoping to the job that actually needs to commit follows least privilege and is flagged by zizmor as excessive-permissions.

🔒️ Proposed fix
-permissions:
-  contents: write
-
 jobs:
   update-badge:
     runs-on: ubuntu-latest
+    permissions:
+      contents: write
     steps:
🧰 Tools
🪛 zizmor (1.26.1)

[error] 7-7: overly broad permissions (excessive-permissions): contents: write is overly broad at the workflow level

(excessive-permissions)


[warning] 7-7: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment

(undocumented-permissions)

🤖 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/update-badge.yml around lines 6 - 7, The workflow-level
contents: write permission is too broad; move it from the top-level workflow
permissions into the specific job that performs the badge update commit. Update
the update-badge workflow so only the relevant job has write access, keeping the
rest of the jobs at the default least-privilege scope.

Source: Linters/SAST tools


jobs:
update-badge:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Pin actions to a commit SHA.

Same concern as in pr-checks.yml: actions/checkout@v4, actions/setup-python@v5, MishaKav/pytest-coverage-comment@v1, and stefanzweifel/git-auto-commit-action@v5 are all unpinned tags per zizmor, allowing a retagged/compromised release to run silently in a job with write access to the repo.

Also applies to: 19-19, 36-36, 47-47

🧰 Tools
🪛 zizmor (1.26.1)

[error] 13-13: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 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/update-badge.yml at line 13, The workflow uses mutable
action tags that should be pinned to commit SHAs for safety. Update the
referenced uses entries in update-badge.yml for actions/checkout,
actions/setup-python, MishaKav/pytest-coverage-comment, and
stefanzweifel/git-auto-commit-action to fixed commit SHAs instead of version
tags, matching the same hardening used in pr-checks.yml. Keep the job behavior
unchanged while replacing each tag with its pinned digest.

Source: Linters/SAST tools

with:
persist-credentials: false
fetch-depth: 0

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"

- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-test.txt

- name: Run tests
run: |
pytest tests/ -v --junitxml=pytest.xml --cov-report=term-missing:skip-covered --cov=minichain | tee pytest-coverage.txt

- name: Coverage comment
id: coverage
uses: MishaKav/pytest-coverage-comment@v1
with:
pytest-coverage-path: ./pytest-coverage.txt
junitxml-path: ./pytest.xml
hide-comment: true

- name: Update README
run: |
sed -i '/<!-- Pytest Coverage Comment:Begin -->/,/<!-- Pytest Coverage Comment:End -->/c\<!-- Pytest Coverage Comment:Begin -->\n\${{ steps.coverage.outputs.coverageHtml }}\n<!-- Pytest Coverage Comment:End -->' ./README.md
Comment on lines +42 to +44

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== update-badge workflow excerpt ==\n'
nl -ba .github/workflows/update-badge.yml | sed -n '1,140p'

printf '\n== search for coverageHtml definition/usages ==\n'
rg -n "coverageHtml|coverageHtml" .github/workflows .github -S

Repository: StabilityNexus/MiniChain

Length of output: 239


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== update-badge workflow excerpt ==\n'
cat -n .github/workflows/update-badge.yml | sed -n '1,160p'

printf '\n== search for coverageHtml definition/usages ==\n'
rg -n "coverageHtml" .github/workflows .github -S

Repository: StabilityNexus/MiniChain

Length of output: 2602


🌐 Web query:

MishaKav pytest-coverage-comment coverageHtml output documentation sanitized raw HTML

💡 Result:

The pytest-coverage-comment GitHub Action by MishaKav provides a coverageHtml output, which is an HTML string containing the full coverage report with clickable links to uncovered lines [1][2]. Regarding the handling of this output as raw HTML: 1. HTML Sanitization: The coverageHtml output contains raw HTML elements (such as links and table structures). If you intend to use this output in contexts that require sanitized or safe HTML—such as displaying it in a GitHub Actions job summary—you generally do not need to sanitize it yourself if you are using standard tools like actions/github-script, which can handle raw HTML strings correctly [3]. 2. Displaying Raw HTML: If you want to include this HTML in a GitHub workflow summary, you can use the actions/github-script tool to write the raw output directly to the job summary [3]. The recommended approach is: - name: Add Summary uses: actions/github-script@v6 with: script: | core.summary.addRaw('${{ steps.coverageComment.outputs.coverageHtml }}').write 3. Troubleshooting Empty Output: If you encounter an empty coverageHtml output, ensure you are using a recent version of the action (v1.1.57 or later), as a known issue regarding empty HTML output when using XML coverage files was resolved in that version [4][5]. Additionally, ensure your pytest command generates the appropriate report files (e.g., --cov-report term-missing) to provide the necessary data for the action to parse [3]. The action also provides options like remove-links-to-files and remove-links-to-lines if you prefer a simpler text-based representation over the default HTML links [2].

Citations:


🌐 Web query:

GitHub Actions escape ${{ in run block backslash expression interpolation

💡 Result:

In GitHub Actions, there is no direct mechanism to "escape" the ${{ sequence within a run block to prevent it from being interpreted as an expression [1][2]. The ${{ }} syntax is processed by the GitHub Actions runner before the shell executes your script [3][2]. Security Best Practice Because ${{ }} is evaluated as a macro before your code runs, attempting to use it for data can lead to serious security vulnerabilities, such as shell injection [3][4][2]. The recommended and secure way to handle data that contains ${{ or other sensitive characters is to avoid interpolating it directly into the run script [4][2]. Instead, you should: 1. Assign the value to an environment variable in the env block of the step [4][2]. 2. Reference that environment variable in your shell script using standard shell syntax (e.g., $MY_VAR) [4][2]. Example of the secure approach: jobs: example-job: runs-on: ubuntu-latest steps: - name: Use data safely env: # Assign the potentially problematic value to an environment variable UNTRUSTED_INPUT: ${{ github.event.pull_request.title }} run: | # Use the environment variable; the shell treats this as data, not code echo "The PR title is: $UNTRUSTED_INPUT" By using this approach, the GitHub Actions evaluator treats the ${{ }} block as a single unit during environment variable assignment, and the shell later treats the variable content strictly as data, effectively neutralizing the risk of code injection [4][2].

Citations:


Move coverageHtml out of the run: script

steps.coverage.outputs.coverageHtml is raw HTML and is interpolated directly into the sed command here. That keeps untrusted content in shell text and can turn report content into command execution in a job with contents: write. Pass it through an env variable and write the README from there instead.

🧰 Tools
🪛 zizmor (1.26.1)

[info] 44-44: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 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/update-badge.yml around lines 42 - 44, The Update README
step in the workflow is interpolating the raw
`steps.coverage.outputs.coverageHtml` directly inside the shell `sed` command,
which should be removed from the script text. Move that output into an `env`
variable for the step, then have the `run:` block read from the environment when
updating `README.md`. Keep the change localized to the Update README step and
preserve the existing `coverageHtml`/README replacement behavior without shell
interpolation.

Source: Linters/SAST tools


- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: 'docs: update coverage badge'
file_pattern: README.md
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@

[![Static Badge](https://img.shields.io/badge/Stability_Nexus-MiniChain-228B22?style=for-the-badge&labelColor=FFC517)](https://stability.nexus/)

<!-- Correct deployed url to be added -->
<!-- Pytest Coverage Comment:Begin -->
<!-- Pytest Coverage Comment:End -->

</div>

Expand Down
3 changes: 3 additions & 0 deletions requirements-test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pytest>=7.0.0
pytest-asyncio>=0.21.0
pytest-cov>=4.0.0
Loading