BUILD-11310 Add report-ci-insights action (per-PR CI metrics comment)#298
BUILD-11310 Add report-ci-insights action (per-PR CI metrics comment)#298mikolaj-matuszny-ext-sonarsource wants to merge 7 commits into
Conversation
Agentic Analysis: Early ResultsAgentic Analysis and Context Augmentation are available on your project. Here are some issues that could have been prevented. Follow the links to learn how to put them into action. 12 issue(s) found across 1 file(s):
Analyzed by SonarQube Agentic Analysis in 2.8 s |
|
| # Escape pipes in the (author-controlled) job name so it can't inject columns. | ||
| name=${name//|/\\|} | ||
| local row="| ${name} |" | ||
| if (( has_cpu )); then row="${row} $(_rci_cpu_cell "$json") |"; fi |
There was a problem hiding this comment.
💡 Security: Rendered metric fields escape only pipes, not newlines/HTML
render_table escapes | in the job name (line 196) and render_cache_fold escapes | in the cache key and backend (lines 254-255), but no other characters are sanitized. These values are produced by jq -r, which converts JSON `` escapes into literal newlines, and they are emitted directly into the markdown table rows of the sticky PR comment.
The metrics JSON is recovered from sibling job stdout (a runner hook the producing job prints), so a field value such as a cache key of "foo malicious row" or one containing HTML like </details> will (a) break the markdown table layout, and (b) inject attacker-influenced markdown/HTML into a comment posted with pull-requests: write. Pipe-escaping alone does not prevent this. Risk is limited because forked-PR GITHUB_TOKEN is read-only, but for same-repo PRs the report is posted with write permission.
Suggested fix: strip/escape newlines (and ideally other control chars) in the same place pipes are escaped, e.g. replace embedded newlines with a space or <br> before building the row. Apply to name, key, and backend.
Collapse embedded CR/LF in author-influenced cells so they cannot break the markdown table or inject extra lines into the comment.:
# in render_table, after the pipe escape:
name=${name//|/\|}
name=${name//$'
'/ }
name=${name//$'\r'/ }
# in render_cache_fold, after the pipe escapes:
key=${key//|/\|}; key=${key//$'
'/ }; key=${key//$'\r'/ }
backend=${backend//|/\|}; backend=${backend//$'
'/ }; backend=${backend//$'\r'/ }
- Apply fix
Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎
Code Review 👍 Approved with suggestions 0 resolved / 1 findingsIntroduces a composite action to aggregate and report CI metrics in a sticky PR comment, including robust sentinel extraction and log-based retrieval. Consider expanding sanitization in the rendering logic to include newlines and HTML characters, as currently only pipe symbols are escaped. 💡 Security: Rendered metric fields escape only pipes, not newlines/HTML📄 report-ci-insights/lib.sh:193-196 📄 report-ci-insights/lib.sh:247-256 render_table escapes The metrics JSON is recovered from sibling job stdout (a runner hook the producing job prints), so a field value such as a cache Suggested fix: strip/escape newlines (and ideally other control chars) in the same place pipes are escaped, e.g. replace embedded newlines with a space or Collapse embedded CR/LF in author-influenced cells so they cannot break the markdown table or inject extra lines into the comment.🤖 Prompt for agentsOptionsAuto-apply is off → Gitar will not commit updates to this branch. Comment with these commands to change:
Was this helpful? React with 👍 / 👎 | Gitar |




What
New
report-ci-insightscomposite action (M4.3). A repository adds it as one dedicated job; it aggregates per-job CI metrics across the workflow run and posts a single sticky PR comment with a compact headline over folded detail.Ticket: BUILD-11310 · Epic: BUILD-11068
How it gets the data (the interesting part)
Both "obvious" transports were empirically ruled out: the hook can't upload an artifact (no runtime token in the hook env — BUILD-11309), and job summaries aren't readable cross-job via any token API. The working transport: the report job (
needs: [all]) downloads each completed sibling's job log viaGET /repos/{r}/actions/jobs/{id}/logs(GITHUB_TOKEN+actions: read) and recovers the metrics JSON the runner hook prints to stdout, wrapped in sentinels (producer PRs: infra#422, this-repo#297). Proven end-to-end in a spike.Usage
Behaviour
<details>. Folding rules: columns with no data are dropped; the Flags column/line appears only when flags exist; the cache fold renders only when a job reported cache.::warning::+ exit 0; never fails the consumer workflow. Skips the report job itself, non-completed jobs, log-download failures, jobs without metrics, and corrupt-JSON records.Tests
32 shellspec examples (mocked
gh) covering sentinel extraction (incl. decoy/hardening), all collect skip paths, folding by absence-assertion, both upsert branches, andmainorchestration. Addedreport-ci-insightsto the.shellspeckcov include-pattern.Reviews
Built per-phase TDD with spec-compliance review each phase, plus a whole-action code-quality review whose two Important findings (validate recovered JSON; escape
|in markdown cells) are fixed with regression tests.Scope
ARC + WarpBuild Linux runners (where the metrics hook runs); PRs only.