Skip to content

chore(security): automated monthly SBOM & VEX report - #965

Open
Gustavohsdp wants to merge 8 commits into
developmentfrom
chore/security-monthly
Open

chore(security): automated monthly SBOM & VEX report#965
Gustavohsdp wants to merge 8 commits into
developmentfrom
chore/security-monthly

Conversation

@Gustavohsdp

@Gustavohsdp Gustavohsdp commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

What this PR adds

Installs the automated software supply-chain security pipeline for openplc-editor (JS / npm; Electron desktop app). Three flows, all driven by the same data:

  • Monthly SBOM & VEX report.github/workflows/security-monthly.yml. On the 1st of each month: regenerates the SBOM (CycloneDX 1.6 + SPDX 2.3), scans against OSV honoring the osv-scanner.toml VEX baseline, renders a report from security/report-config.json, opens a PR with the dated snapshot under security/<YYYY-MM>/. Runs against the default branch (development).
  • PR security gate.github/workflows/security-pr-gate.yml. On every PR: compares base vs head and fails only when the PR introduces a NEW HIGH/CRITICAL advisory (pre-existing never blocks). Posts a sticky comment with package, version, advisory link, fixed version, and how to resolve.
  • Archive on merge.github/workflows/security-pr-archive.yml. On merge, commits the SBOM + report under security/pr-<number>-<date>/.

Supporting files: scripts/, osv-scanner.toml (VEX baseline), security/report-config.json, security/README.md.

Security posture (assessment)

1,701 components. Of 143 raw advisories: 3 warrant remediation (ws + socket.io-parser via socket.io-client — DoS reachable only through the optional live-debug WebSocket; not exploitable in normal editing), 37 mitigated (electron — neutralized by a hardened Electron config; dompurify — safe default config), 103 not applicable. The 3 critical advisories are all in build/packaging tooling pruned from the shipped app.

What to review

  • osv-scanner.toml + security/report-config.json — the VEX triage (note: electron is a devDependency but its Chromium runtime is shipped, so it's assessed as in-scope). Best reviewed by product security (Thiago).
  • .github/workflows/security-*.yml — the three flows.

Notes

  • Not enforced yet: the gate reports pass/fail but does not block merges until set as a required status check.
  • Claude triage authenticates via the org secret CLAUDE_CODE_OAUTH_TOKEN.

Summary by CodeRabbit

  • New Features

    • Added automated software bill of materials (SBOM) generation in CycloneDX and SPDX formats.
    • Added vulnerability scanning and pull request security checks that flag newly introduced high-severity advisories.
    • Added monthly security reports with component inventories, vulnerability findings, and remediation guidance.
    • Added archived security snapshots for monthly releases and merged pull requests.
    • Added configurable vulnerability triage and reporting workflows.
  • Documentation

    • Added guidance for security artifacts, automation, reviews, and report management.

Ports the validated supply-chain automation to openplc-editor (public repo — no
AI triage; the Claude step is skipped gracefully with no token, deterministic
SBOM + report + PR still run). Regenerates the SBOM (CycloneDX+SPDX), scans OSV
honoring the VEX baseline, renders the report, opens a monthly PR. Baseline
suppresses the 146 known not-affected/mitigated IDs; ws & socket.io-parser
(debug-WebSocket DoS, P2) are left visible for action.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AejAiL4tfXCyMhwtjY4vFT
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds security automation for SBOM generation, vulnerability scanning, suppression baselines, report rendering, monthly snapshots, pull request gating, and merged pull request archives.

Changes

Security automation and reporting

Layer / File(s) Summary
Security configuration and suppression baseline
.gitignore, osv-scanner.toml, security/report-config.json, scripts/gen-osv-ignores.mjs
Defines report metadata, time-bounded VEX suppressions, verdict-based suppression generation, and ignored transient SBOM output.
SBOM generation and vulnerability scanning
scripts/generate-sbom.sh, scripts/cdx-to-spdx.mjs, scripts/scan-vulns.mjs
Generates CycloneDX and SPDX SBOMs, component inventory CSV data, and vulnerability CSV data.
Report rendering and monthly snapshots
scripts/build-report.mjs, .github/workflows/security-monthly.yml, security/README.md
Builds Markdown, HTML, and PDF reports, archives dated monthly artifacts, and opens review pull requests.
Pull request advisory gate
.github/workflows/security-pr-gate.yml, scripts/pr-gate-diff.mjs
Compares base and head OSV results, evaluates introduced advisories against HIGH, and updates a pull request comment.
Merged pull request archive
.github/workflows/security-pr-archive.yml
Generates per-PR security artifacts after merges and commits changed archives to the base branch.

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

Sequence Diagram(s)

sequenceDiagram
  participant PullRequest
  participant SecurityPRGate
  participant OSVScanner
  participant PRGateDiff
  PullRequest->>SecurityPRGate: trigger the security gate
  SecurityPRGate->>OSVScanner: scan base and head
  SecurityPRGate->>PRGateDiff: compare introduced advisories
  PRGateDiff-->>SecurityPRGate: return the gate status
  SecurityPRGate->>PullRequest: update the security comment
Loading

Poem

I’m a rabbit with a report in my paws,
SBOMs hop neatly without flaws.
VEX entries rest, alerts stay bright,
Pull requests guard the code at night.
Snapshots bloom month by month—
Security carrots for everyone!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: automated monthly SBOM and VEX reporting for security.
Description check ✅ Passed The description clearly explains the three workflows, supporting files, security assessment, review focus, and current merge-check limitation.
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 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/security-monthly

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
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 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/security-monthly.yml:
- Around line 59-63: Update the Scan step in
.github/workflows/security-monthly.yml at lines 59-63 to preserve osv-scanner’s
exit status when /tmp/osv.json is missing or invalid, while continuing only for
the scanner status that indicates findings were detected. Update
scripts/scan-vulns.mjs at lines 8-10 so unreadable or malformed input exits
nonzero rather than producing an empty CSV.
- Around line 65-89: Remove the Claude OAuth triage step and its associated
token setup guidance from .github/workflows/security-monthly.yml and
security/README.md, including the anthropics/claude-code-action invocation,
token references, and instructions for configuring the token. Preserve the
deterministic monthly report generation and human PR review workflow; do not
alter unrelated security scanning or reporting behavior.
- Around line 120-134: Update the “Open Pull Request” step using
peter-evans/create-pull-request@v6 to explicitly set its base branch to
development. Keep the existing branch, title, labels, commit message, and body
configuration unchanged.

In `@scripts/gen-osv-ignores.mjs`:
- Around line 13-16: Update the advisory filtering logic in the OSV
ignore-generation flow to require a verdict column before emitting any ignores.
When no recognized verdict field is present, exit without suppressing any
findings; when present, retain only explicitly triaged not-affected rows and
exclude actionable results such as ws/socket.io-parser.

In `@scripts/generate-sbom.sh`:
- Around line 36-42: Pin and verify both security tools before execution: in
scripts/generate-sbom.sh, replace the mutable `@cyclonedx/cdxgen`@11 npx install
with an exact cdxgen package version and checksum/signature validation; in
.github/workflows/security-monthly.yml, replace the /releases/latest OSV Scanner
download with an exact release and verify its checksum or signature before
running it.

In `@security/report-config.json`:
- Around line 9-19: The report currently hard-codes provenance and advisory
totals without reconciling them with the OSV scan and suppression baseline. In
security/report-config.json lines 9-19, regenerate or derive the advisory
source, totals, and status metrics from the snapshot’s actual scan and baseline;
in scripts/build-report.mjs lines 62-66, consume validated scan metrics and fail
the report build when configured values do not match the current scan or VEX
baseline, including tokenless or failed triage results.
🪄 Autofix (Beta)

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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 129bc7fd-9ac4-43e6-b345-c2b79c73dede

📥 Commits

Reviewing files that changed from the base of the PR and between 89998a1 and 645381c.

📒 Files selected for processing (10)
  • .github/workflows/security-monthly.yml
  • .gitignore
  • osv-scanner.toml
  • scripts/build-report.mjs
  • scripts/cdx-to-spdx.mjs
  • scripts/gen-osv-ignores.mjs
  • scripts/generate-sbom.sh
  • scripts/scan-vulns.mjs
  • security/README.md
  • security/report-config.json

Comment thread .github/workflows/security-monthly.yml
Comment thread .github/workflows/security-monthly.yml Outdated
Comment thread .github/workflows/security-monthly.yml
Comment thread scripts/gen-osv-ignores.mjs
Comment thread scripts/generate-sbom.sh Outdated
Comment thread security/report-config.json Outdated

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 8

🧹 Nitpick comments (3)
.github/workflows/security-pr-gate.yml (2)

74-75: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reconsider pushing archive commits onto contributor PR branches.

Beyond the required-check interaction, this mutates the author's branch mid-review (force-push races, stale local clones, review churn on every push) and stores per-PR SBOM history in the repo permanently. Uploading the same bundle via actions/upload-artifact keeps the audit trail without touching the branch, with archiving to security/ reserved for the monthly workflow in .github/workflows/security-monthly.yml.

🤖 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/security-pr-gate.yml around lines 74 - 75, Update the
“Archive SBOM & report for this PR” step in the security PR gate to stop
committing or pushing archive changes to contributor branches. Replace branch
mutation with actions/upload-artifact for the generated SBOM and report, while
leaving security/ archiving to the monthly workflow.

76-81: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Unguarded ls parsing and no set -euo pipefail — a missing SBOM silently produces a garbage archive path.

If sbom/*.cdx.json doesn't exist, ls errors, NAME becomes empty, and the step continues: build-report.mjs runs against sbom/.cdx.json, cp fails, and (without set -e) the step's success depends only on the last command. Assert the file exists explicitly.

♻️ Proposed fix
         run: |
+          set -euo pipefail
           bash scripts/generate-sbom.sh
-          NAME=$(basename "$(ls sbom/*.cdx.json | head -1)" .cdx.json)
+          shopt -s nullglob
+          CDX=(sbom/*.cdx.json)
+          if [ ${`#CDX`[@]} -eq 0 ]; then
+            echo "::error::no CycloneDX SBOM produced by generate-sbom.sh"; exit 1
+          fi
+          NAME=$(basename "${CDX[0]}" .cdx.json)
🤖 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/security-pr-gate.yml around lines 76 - 81, Harden the
workflow shell block by enabling `set -euo pipefail` and explicitly validating
that an SBOM matching `sbom/*.cdx.json` exists before deriving `NAME`. Replace
the unguarded `ls` parsing with a checked file-selection flow, and ensure
subsequent `build-report.mjs`/copy commands cannot run with an empty SBOM path.
scripts/pr-gate-diff.mjs (1)

50-57: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider keying by id + package@version rather than advisory id alone.

With id-only keys, a PR that adds a new dependency already covered by an advisory present in BASE (same GHSA, different package) is treated as pre-existing and passes. Keying on ${v.id}|${pkg} catches newly-affected packages while still ignoring truly pre-existing findings; report grouping can still be by id.

Also note advisory aliases (GHSA ↔ CVE) — if base and head ever resolve the same issue under different ids, it would be reported as introduced. Folding v.aliases into the lookup makes the comparison more robust.

🤖 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 `@scripts/pr-gate-diff.mjs` around lines 50 - 57, Update index to key each
vulnerability by its advisory identifier plus the affected package name and
version, rather than by v.id alone, so the same advisory on a newly introduced
dependency is retained. Include vulnerability aliases from v.aliases in the
lookup keys so GHSA/CVE representations match across base and head, while
preserving report grouping by the primary advisory id.
🤖 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/security-pr-gate.yml:
- Around line 13-16: Remove the paths-ignore filter at
.github/workflows/security-pr-gate.yml lines 13-16 so every pull-request head
SHA reports the required gate check. At lines 88-96, preserve the existing [skip
ci] protection or use actions/upload-artifact to prevent archive commits from
retriggering the workflow; ensure the job short-circuits internally while still
producing a gate conclusion.
- Around line 54-65: The HEAD and BASE scan commands currently suppress all
osv-scanner failures, allowing missing or invalid reports to pass the gate.
Update the scan steps to capture and explicitly handle osv-scanner exit codes,
treating the vulnerability-found status as valid while failing on execution
errors, then verify /tmp/head.json and /tmp/base.json exist and contain valid
JSON before proceeding.
- Around line 88-96: Update the workflow’s inline shell to receive both the pull
request head ref and the PR number through step-level environment variables
rather than direct GitHub expression interpolation. Reference those variables in
the existing quoted git commit and push commands, preserving the current SBOM
archive behavior while preventing branch-name or PR-value shell injection.
- Around line 47-50: Update the “Install osv-scanner” step to download a fixed
osv-scanner release asset instead of the mutable releases/latest URL, then fetch
the matching release checksum and verify the downloaded binary before moving or
installing it at /usr/local/bin/osv-scanner. Keep the existing executable
installation behavior and ensure verification fails the workflow on mismatch.

In `@scripts/pr-gate-diff.mjs`:
- Around line 11-15: The scan gate currently fails open by masking scanner,
file-read, and JSON-parse failures. In scripts/pr-gate-diff.mjs lines 11-15,
require baseP and headP arguments and update load to report the underlying error
and exit non-zero instead of returning an empty object; in
.github/workflows/security-pr-gate.yml lines 54-65, remove blanket || true,
propagate osv-scanner failures except its documented vulnerability-found exit
code, and verify /tmp/head.json and /tmp/base.json exist and are non-empty
before running the gate.
- Line 61: Validate THRESHOLD before using ORDER.indexOf in the threshold
evaluation flow: reject unrecognized values such as MEDIUM or high-severity with
a clear configuration error, rather than allowing min = -1 to block every
advisory. Preserve the existing behavior for recognized ORDER values.
- Around line 38-48: Update severityOf so the database_specific severity word is
normalized from MEDIUM to MODERATE before checking ORDER.includes. Preserve the
existing ORDER-based return behavior and conservative HIGH fallback for
unrecognized or unscored advisories.
- Around line 30-34: Update the roundup helper in the score calculation to
follow CVSS v3.1’s integer-based Roundup: truncate or floor x multiplied by
100000, then compute the ceiling to one decimal from that integer value. Replace
the current Math.ceil-based implementation while preserving the existing score
capping and bucket behavior in the surrounding calculation.

---

Nitpick comments:
In @.github/workflows/security-pr-gate.yml:
- Around line 74-75: Update the “Archive SBOM & report for this PR” step in the
security PR gate to stop committing or pushing archive changes to contributor
branches. Replace branch mutation with actions/upload-artifact for the generated
SBOM and report, while leaving security/ archiving to the monthly workflow.
- Around line 76-81: Harden the workflow shell block by enabling `set -euo
pipefail` and explicitly validating that an SBOM matching `sbom/*.cdx.json`
exists before deriving `NAME`. Replace the unguarded `ls` parsing with a checked
file-selection flow, and ensure subsequent `build-report.mjs`/copy commands
cannot run with an empty SBOM path.

In `@scripts/pr-gate-diff.mjs`:
- Around line 50-57: Update index to key each vulnerability by its advisory
identifier plus the affected package name and version, rather than by v.id
alone, so the same advisory on a newly introduced dependency is retained.
Include vulnerability aliases from v.aliases in the lookup keys so GHSA/CVE
representations match across base and head, while preserving report grouping by
the primary advisory id.
🪄 Autofix (Beta)

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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d8358098-c006-4c4c-a7a7-d8f10a07e689

📥 Commits

Reviewing files that changed from the base of the PR and between 645381c and f52e054.

📒 Files selected for processing (2)
  • .github/workflows/security-pr-gate.yml
  • scripts/pr-gate-diff.mjs

Comment thread .github/workflows/security-pr-gate.yml Outdated
Comment thread .github/workflows/security-pr-gate.yml Outdated
Comment thread .github/workflows/security-pr-gate.yml Outdated
Comment thread .github/workflows/security-pr-gate.yml Outdated
Comment thread scripts/pr-gate-diff.mjs Outdated
Comment thread scripts/pr-gate-diff.mjs
Comment thread scripts/pr-gate-diff.mjs
Comment thread scripts/pr-gate-diff.mjs

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 7

🤖 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 `@security/pr-965-2026-07-29/OpenPLC-Editor-Desktop-Security-Report.md`:
- Around line 57-59: The affected-component versions are not auditable because
the Installed column only states the dependency path. Update both
security/pr-965-2026-07-29/OpenPLC-Editor-Desktop-Security-Report.md lines 57-59
and security/pr-965-2026-07-29/OpenPLC-Editor-Desktop-Security-Report.html lines
54-55 consistently by adding exact installed versions for ws and
socket.io-parser, or renaming the column to reflect that it contains dependency
provenance.
- Line 38: Update the methodology text in
security/pr-965-2026-07-29/OpenPLC-Editor-Desktop-Security-Report.md at lines
38-38 to accurately describe the osv-scanner workflow and its advisory-data
provenance instead of npm audit/GitHub Dependabot. Regenerate
security/pr-965-2026-07-29/OpenPLC-Editor-Desktop-Security-Report.html at lines
40-40 from the corrected Markdown methodology, preserving the same provenance
description in both reports.
- Line 59: Update the remediation documentation to address the TLS trust bypass:
in security/pr-965-2026-07-29/OpenPLC-Editor-Desktop-Security-Report.md lines 59
and 81-83, retain the rejectUnauthorized:false finding and add certificate
validation/pinning or explicitly document the accepted risk; synchronize the
same complete finding and remediation plan in
security/pr-965-2026-07-29/OpenPLC-Editor-Desktop-Security-Report.html lines 55
and 69.
- Line 5: Update the authoritative application version from v4.2.9 to v4.2.10 in
security/pr-965-2026-07-29/OpenPLC-Editor-Desktop-Security-Report.md at line 5,
then regenerate
security/pr-965-2026-07-29/OpenPLC-Editor-Desktop-Security-Report.html at line
20 from the corrected source so both reports identify v4.2.10.

In `@src/frontend/components/_organisms/debugger/index.tsx`:
- Around line 38-39: Move the valuesRef.current assignment out of the component
render body and synchronize it in a useEffect declared before the sampling
effect. Keep the ref aligned with debugBoolValues and debugNonBoolValues while
ensuring the sampler only reads committed state.

In `@src/frontend/store/slices/project/slice.ts`:
- Around line 324-345: Update getMemoizedAliasIndex to include
live.deviceAvailableOptions.availableBoards in the cache key and identity check,
matching its use by buildIecRegistry. Store the tracked availableBoards
reference in aliasIndexCache so capability or catalog updates invalidate the
memoized alias index.
- Around line 298-322: Move the aliasIndexCache declaration from module scope
into the createProjectSlice factory, preserving the existing AliasIndexCache
type and cache comparison/rebuild behavior. Ensure all alias-index access within
createProjectSlice uses this per-instance variable so separate store instances
never share cached entries.
🪄 Autofix (Beta)

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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 27e9a878-aa4a-4914-92ce-944c35cc373d

📥 Commits

Reviewing files that changed from the base of the PR and between f52e054 and d71520e.

⛔ Files ignored due to path filters (3)
  • package-lock.json is excluded by !**/package-lock.json
  • security/pr-965-2026-07-29/sbom/open-plc-editor.components.csv is excluded by !**/*.csv
  • security/pr-965-2026-07-29/sbom/vulnerabilities.csv is excluded by !**/*.csv
📒 Files selected for processing (26)
  • binary-versions.json
  • package.json
  • security/pr-965-2026-07-29/OpenPLC-Editor-Desktop-Security-Report.html
  • security/pr-965-2026-07-29/OpenPLC-Editor-Desktop-Security-Report.md
  • security/pr-965-2026-07-29/sbom/open-plc-editor.cdx.json
  • security/pr-965-2026-07-29/sbom/open-plc-editor.spdx.json
  • src/frontend/components/_molecules/charts/line-chart.tsx
  • src/frontend/components/_organisms/debugger/index.tsx
  • src/frontend/components/_organisms/variables-editor/__tests__/return-type-visibility.test.tsx
  • src/frontend/components/_organisms/variables-editor/index.tsx
  • src/frontend/data/constants/app-version.ts
  • src/frontend/hooks/useDebugPolling.ts
  • src/frontend/services/graphical-scope.ts
  • src/frontend/services/st-lsp/__tests__/project-sync.test.ts
  • src/frontend/services/st-lsp/index.ts
  • src/frontend/services/st-lsp/project-sync.ts
  • src/frontend/store/__tests__/project-slice.test.ts
  • src/frontend/store/slices/project/slice.ts
  • src/frontend/store/slices/project/types.ts
  • src/frontend/utils/PLC/__tests__/array-variable-utils.test.ts
  • src/frontend/utils/PLC/__tests__/pou-signature-serializer.test.ts
  • src/frontend/utils/PLC/__tests__/resource-globals-serializer.test.ts
  • src/frontend/utils/PLC/array-variable-utils.ts
  • src/frontend/utils/PLC/pou-signature-serializer.ts
  • src/frontend/utils/PLC/resource-globals-serializer.ts
  • src/middleware/shared/ports/platform-capabilities.ts
💤 Files with no reviewable changes (2)
  • src/frontend/utils/PLC/tests/array-variable-utils.test.ts
  • src/frontend/utils/PLC/array-variable-utils.ts
👮 Files not reviewed due to content moderation or server errors (1)
  • security/pr-965-2026-07-29/sbom/open-plc-editor.spdx.json

Comment thread security/pr-965-2026-07-29/OpenPLC-Editor-Desktop-Security-Report.md Outdated
Comment thread security/pr-965-2026-07-29/OpenPLC-Editor-Desktop-Security-Report.md Outdated
Comment thread security/pr-965-2026-07-29/OpenPLC-Editor-Desktop-Security-Report.md Outdated
Comment thread security/pr-965-2026-07-29/OpenPLC-Editor-Desktop-Security-Report.md Outdated
Comment thread src/frontend/components/_organisms/debugger/index.tsx
Comment thread src/frontend/store/slices/project/slice.ts
Comment thread src/frontend/store/slices/project/slice.ts
…erge

Gate now only scans/reports (contents: read) and posts one actionable sticky PR
comment; per-PR SBOM archived on MERGE by security-pr-archive.yml. Validated on
autonomy-edge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AejAiL4tfXCyMhwtjY4vFT
@Gustavohsdp
Gustavohsdp force-pushed the chore/security-monthly branch from d71520e to 010b001 Compare July 31, 2026 11:38

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 5

🤖 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/security-pr-archive.yml:
- Around line 12-14: Restrict the pull_request trigger in the workflow’s on
configuration to the development base branch by adding the branches filter
alongside types: [closed]. Preserve the existing closed-event behavior while
preventing archive and push steps from running for other branches.
- Around line 28-32: Update the checkout step to use the closed pull request’s
immutable merged commit SHA rather than github.event.pull_request.base.ref for
SBOM generation. In the archive commit/push flow around the existing lines
60-67, fetch and rebase onto the latest base branch before pushing, with retry
handling for non-fast-forward updates.
- Around line 42-45: Update the “Install osv-scanner” step to download a
specific immutable OSV Scanner release instead of the mutable latest asset, then
verify the binary against the release-provided SHA256 checksum or SLSA
provenance before running chmod. Ensure verification failure stops the workflow
and only make the binary executable after successful validation.
- Around line 52-53: Update the OSV scan step around the osv-scanner invocation
and scripts/scan-vulns.mjs call to capture the scanner exit status, allow only
statuses 0 and 1, and fail the job for any other status. Before invoking
scan-vulns.mjs, validate that /tmp/osv.json is nonempty and contains the
data.results field; stop the workflow when validation fails.

In @.github/workflows/security-pr-gate.yml:
- Around line 37-41: Update the actions/checkout@v4 configuration in the
“Checkout PR head” step to disable persisted credentials, keeping the workflow
token out of the local Git configuration; continue supplying GH_TOKEN only to
the later comment step.
🪄 Autofix (Beta)

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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8f3a6a7c-056f-4332-9c5a-4a0427285b8a

📥 Commits

Reviewing files that changed from the base of the PR and between d71520e and 010b001.

📒 Files selected for processing (3)
  • .github/workflows/security-pr-archive.yml
  • .github/workflows/security-pr-gate.yml
  • scripts/pr-gate-diff.mjs

Comment thread .github/workflows/security-pr-archive.yml Outdated
Comment thread .github/workflows/security-pr-archive.yml Outdated
Comment thread .github/workflows/security-pr-archive.yml Outdated
Comment thread .github/workflows/security-pr-archive.yml Outdated
Comment thread .github/workflows/security-pr-gate.yml
Comment thread .github/workflows/security-pr-gate.yml Outdated
Comment thread .github/workflows/security-pr-archive.yml Outdated
Comment thread .github/workflows/security-pr-archive.yml Outdated
Comment thread .github/workflows/security-monthly.yml
Comment thread .github/workflows/security-pr-archive.yml Outdated
Comment thread scripts/pr-gate-diff.mjs
Comment thread scripts/generate-sbom.sh Outdated
Comment thread scripts/pr-gate-diff.mjs Outdated
Gate fails closed on scanner error; MEDIUM no longer blocks as HIGH; CVSS 4.0
handled conservatively; diff keyed by advisory id + package. Report HTML escaped
(+ PDF rendered with JS disabled). cdx-to-spdx dedupes packages and uses OR for
dual licenses. gen-osv-ignores requires a verdict column. Validated on autonomy-edge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AejAiL4tfXCyMhwtjY4vFT

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
.github/workflows/security-monthly.yml (4)

26-31: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Sensitive Data Exposure (CWE-522): Insufficiently Protected Credentials

Reachability: External

Do not expose the OAuth token to the whole job.

CLAUDE_CODE_OAUTH_TOKEN is inherited by every later step, including repository scripts and external actions. A compromised command can read the token even when the Claude step is skipped. Isolate triage in a separate job, or pass the secret only to the Claude action after a non-secret gate.

🤖 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/security-monthly.yml around lines 26 - 31, Restrict
CLAUDE_CODE_OAUTH_TOKEN in the report workflow so it is not defined at the
job-level env and inherited by all steps. Isolate the Claude triage into a
separate job or pass the secret only within the Claude action step, while
preserving the existing non-secret gate that controls whether triage runs.

133-135: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Make the triage statement conditional.

When the OAuth token is absent, the Claude step is skipped. When the action fails, continue-on-error: true still allows the workflow to continue. The unconditional PR text says that new advisories were triaged. Remove that claim or generate it only after a successful triage result.

🤖 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/security-monthly.yml around lines 133 - 135, Update the
generated security report text in the monthly workflow so it does not
unconditionally claim that advisories were triaged; omit that bullet when the
Claude triage step is skipped or fails, and include it only when the triage step
completes successfully. Use the existing Claude step result/status symbol to
drive this condition while preserving the other report bullets.

81-99: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Regenerate vulnerability artifacts after triage configuration changes.

Claude can edit osv-scanner.toml and security/report-config.json after osv-scanner scan and node scripts/scan-vulns.mjs write sbom/vulnerabilities.csv, then the report links that pre-triage CSV. Rerun the scan/CSV conversion after accepted triage edits or base the archive/report on one post-triage dataset.

🤖 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/security-monthly.yml around lines 81 - 99, Update the
workflow’s triage and report-generation sequence so vulnerability scanning and
CSV conversion run again after Claude edits osv-scanner.toml or
security/report-config.json, ensuring the archived and linked
vulnerabilities.csv reflects post-triage results. Keep the existing report
generation based on that regenerated dataset, and preserve the instruction to
edit only the two triage configuration files.

55-58: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Security Misconfiguration (CWE-494): Download of Code Without Integrity Check

Reachability: External

Pin and verify the OSV scanner binary.

This job downloads the moving latest release with only HTTPS, then executes it in a workflow that has contents: write and pull-requests: write. Pin a known release and verify the release checksum or provenance before installation.

🤖 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/security-monthly.yml around lines 55 - 58, Update the
“Install osv-scanner” workflow step to download a fixed, known OSV Scanner
release instead of the moving latest URL, then verify the downloaded binary
against the release checksum or provenance before making it executable and
installing it. Keep execution blocked when verification fails, and retain the
existing osv-scanner command path.
♻️ Duplicate comments (4)
.github/workflows/security-monthly.yml (4)

123-137: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Set development as the pull-request base.

The workflow supports manual runs and checks out the triggering ref. Without base: development, create-pull-request@v6 can target the checked-out or repository default branch instead of development.

Proposed fix
         with:
+          base: development
           branch: chore/security-${{ steps.m.outputs.month }}
🤖 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/security-monthly.yml around lines 123 - 137, Update the
“Open Pull Request” step using peter-evans/create-pull-request@v6 to explicitly
set its base branch to development, while preserving the existing branch, title,
labels, commit message, and body configuration.

65-89: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

LLM Security (CWE-269): Improper Privilege Management

Reachability: External

Remove the OAuth-backed AI triage step.

anthropics/claude-code-action@v1 receives a personal OAuth token and can modify the VEX baseline and report data with repository write permissions. The prompt’s EDIT ONLY instruction is not a permission boundary. continue-on-error: true can also allow partial security-data changes to reach the pull request. Keep this process deterministic and human-reviewed.

🤖 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/security-monthly.yml around lines 65 - 89, Remove the
entire Claude OAuth triage step identified by “Claude — triage new advisories &
update report data,” including its conditional, continue-on-error setting,
action invocation, token input, and prompt; leave the deterministic security
report generation steps unchanged.

59-63: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Security Misconfiguration (CWE-693)

Reachability: External

Preserve scanner failures instead of treating them as clean results.

|| true discards every osv-scanner exit status. Continue only for the scanner status that means “findings detected.” Fail for all other statuses, and validate that /tmp/osv.json is present and valid before generating the CSV.

🤖 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/security-monthly.yml around lines 59 - 63, Update the
“Scan” workflow step so it captures the exit status from osv-scanner, continues
only when that status means findings were detected, and fails for all other
nonzero statuses. Before invoking scan-vulns.mjs, validate that /tmp/osv.json
exists and contains valid scanner JSON; do not generate the CSV from missing or
invalid output.

123-137: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Ensure generated pull requests receive their required checks.

Because token: is omitted, create-pull-request@v6 uses GITHUB_TOKEN. GitHub does not start downstream workflow runs for events created with that token. If the generated PR requires security-pr-gate.yml or other required checks, it can remain unmergeable. Use a minimally scoped GitHub App installation token or document the required manual retrigger.

🤖 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/security-monthly.yml around lines 123 - 137, Update the
“Open Pull Request” step using peter-evans/create-pull-request@v6 to
authenticate with a minimally scoped GitHub App installation token instead of
the default GITHUB_TOKEN, so downstream required-check workflows such as
security-pr-gate.yml run for generated pull requests. If an app token cannot be
provided in the workflow, document the required manual retrigger procedure in
this step.
🤖 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 `@scripts/gen-osv-ignores.mjs`:
- Around line 40-42: Update isNotAffected to normalize whitespace and compare
only exact canonical VEX status values such as not_affected and not_applicable,
rather than matching substrings. Exclude reason text and do not treat mitigated
as suppressed unless it is an explicitly supported canonical status; keep
affected or ambiguous values visible.
- Around line 43-48: Update the row-processing logic around seen and
isNotAffected so advisories are grouped by OSV ID and an ignore entry is emitted
only when every package/version row for that ID has an explicitly suppressible
verdict. Do not let a single not_affected row mark an advisory seen or suppress
actionable or ambiguous rows sharing the same ID; preserve the existing
generated [[IgnoredVulns]] output format.

---

Outside diff comments:
In @.github/workflows/security-monthly.yml:
- Around line 26-31: Restrict CLAUDE_CODE_OAUTH_TOKEN in the report workflow so
it is not defined at the job-level env and inherited by all steps. Isolate the
Claude triage into a separate job or pass the secret only within the Claude
action step, while preserving the existing non-secret gate that controls whether
triage runs.
- Around line 133-135: Update the generated security report text in the monthly
workflow so it does not unconditionally claim that advisories were triaged; omit
that bullet when the Claude triage step is skipped or fails, and include it only
when the triage step completes successfully. Use the existing Claude step
result/status symbol to drive this condition while preserving the other report
bullets.
- Around line 81-99: Update the workflow’s triage and report-generation sequence
so vulnerability scanning and CSV conversion run again after Claude edits
osv-scanner.toml or security/report-config.json, ensuring the archived and
linked vulnerabilities.csv reflects post-triage results. Keep the existing
report generation based on that regenerated dataset, and preserve the
instruction to edit only the two triage configuration files.
- Around line 55-58: Update the “Install osv-scanner” workflow step to download
a fixed, known OSV Scanner release instead of the moving latest URL, then verify
the downloaded binary against the release checksum or provenance before making
it executable and installing it. Keep execution blocked when verification fails,
and retain the existing osv-scanner command path.

---

Duplicate comments:
In @.github/workflows/security-monthly.yml:
- Around line 123-137: Update the “Open Pull Request” step using
peter-evans/create-pull-request@v6 to explicitly set its base branch to
development, while preserving the existing branch, title, labels, commit
message, and body configuration.
- Around line 65-89: Remove the entire Claude OAuth triage step identified by
“Claude — triage new advisories & update report data,” including its
conditional, continue-on-error setting, action invocation, token input, and
prompt; leave the deterministic security report generation steps unchanged.
- Around line 59-63: Update the “Scan” workflow step so it captures the exit
status from osv-scanner, continues only when that status means findings were
detected, and fails for all other nonzero statuses. Before invoking
scan-vulns.mjs, validate that /tmp/osv.json exists and contains valid scanner
JSON; do not generate the CSV from missing or invalid output.
- Around line 123-137: Update the “Open Pull Request” step using
peter-evans/create-pull-request@v6 to authenticate with a minimally scoped
GitHub App installation token instead of the default GITHUB_TOKEN, so downstream
required-check workflows such as security-pr-gate.yml run for generated pull
requests. If an app token cannot be provided in the workflow, document the
required manual retrigger procedure in this step.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c5506f4-4159-4a09-affe-9c6e11f7fe4a

📥 Commits

Reviewing files that changed from the base of the PR and between 010b001 and bbd5e96.

📒 Files selected for processing (6)
  • .github/workflows/security-monthly.yml
  • .github/workflows/security-pr-gate.yml
  • scripts/build-report.mjs
  • scripts/cdx-to-spdx.mjs
  • scripts/gen-osv-ignores.mjs
  • scripts/pr-gate-diff.mjs
🚧 Files skipped from review as they are similar to previous changes (3)
  • .github/workflows/security-pr-gate.yml
  • scripts/build-report.mjs
  • scripts/cdx-to-spdx.mjs

Comment thread scripts/gen-osv-ignores.mjs
Comment thread scripts/gen-osv-ignores.mjs
Gustavohsdp and others added 4 commits August 4, 2026 10:16
Scan fail-closed + monthly exit-code; osv v2.4.0 (+SHA256) & cdxgen 11.11.0
pinned; node:test in the gate; cdx-to-csv.mjs; OSV methodology text; explicit
base. Public repo: removed the Claude/OAuth triage step + token guidance
(deterministic report + human review only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AejAiL4tfXCyMhwtjY4vFT
…token fallback (public: no AI)

build-report derives headline metrics from the live scan; monthly runs raw+delta;
archive -> push-on-default + orphan security-archive (SBOM & raw scan only);
monthly PR uses SECURITY_BOT_TOKEN fallback. Public repo: Claude triage kept out.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AejAiL4tfXCyMhwtjY4vFT
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AejAiL4tfXCyMhwtjY4vFT
@Gustavohsdp

Copy link
Copy Markdown
Contributor Author

Review feedback addressed ✅

Thanks for the thorough review. Every point raised has been addressed across follow-up commits — the gate check is green and the branch is up to date.

Correctness / security bugs

  • PR gate now fails closed — a scanner error (missing/invalid JSON, or an exit code other than 0/1) hard-fails the step instead of silently passing (“no new vulnerabilities”) or blocking every advisory.
  • SeverityMEDIUM is normalized to MODERATE (no longer blocks as HIGH); CVSS 4.0 is treated conservatively and flagged; the CVSS 3.1 base-score formula cites the spec (§7.1).
  • Diff keyed by advisory id + package — the same CVE on a new package now counts as introduced; a version bump of an already-vulnerable package does not.
  • Report HTML injection — config strings are escaped (only attribute-less <b>/<code>/… re-enabled) and the PDF renders with --disable-javascript.
  • SPDX — components are de-duplicated by ref (no more two packages sharing one SPDXID); multiple licenses join with OR (dual-licensed), not AND.
  • gen-osv-ignores requires a verdict column and refuses to run without one (can no longer suppress the entire scan).
  • Monthly scan / scan-vulns fail closed too — a broken scan can no longer produce a clean report.

Supply-chain / hardening

  • osv-scanner pinned to v2.4.0 and verified against its SHA256SUMS; cdxgen / cyclonedx-py pinned to exact versions.
  • Inline node -e CSV builder extracted to scripts/cdx-to-csv.mjs.
  • Unit tests (node:test) for the gate diff + SPDX, executed inside the gate.
  • Report methodology corrected to OSV / osv-scanner (was “npm/pnpm/pip audit”).

Report & archive integrity

  • The report’s headline numbers are now derived from the live scan (raw vs. VEX-suppressed vs. surfacing), so it cannot attest stale figures.
  • The per-PR archive moved to a push-on-default-branch trigger (writable token — works for merged fork PRs) that stores SBOM + raw scan only on a dedicated orphan security-archive branch — no repo/history bloat and no false attestation.
  • The monthly PR uses secrets.SECURITY_BOT_TOKEN (with GITHUB_TOKEN fallback) so its own checks can run.

Since this is a public repository, the optional Claude/OAuth triage step and its token guidance were removed — the monthly report is deterministic + human-reviewed.

Two notes

  1. The monthly and archive workflows run on cron / push-to-default, so they execute for the first time after merge — the deterministic pieces are validated locally and the gate is green, but they don’t run on this PR itself.
  2. To make the monthly PR trigger checks, a one-time SECURITY_BOT_TOKEN secret (a GitHub App installation token or a fine-grained PAT with contents + PR write) is needed; without it the PR still opens, just without checks.

Ready for another look 🙏

@JoaoGSP
JoaoGSP self-requested a review August 5, 2026 15:56

@JoaoGSP JoaoGSP left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

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.

2 participants