Add JaCoCo test coverage configuration and CI reporting#448
Add JaCoCo test coverage configuration and CI reporting#448devin-ai-integration[bot] wants to merge 5 commits into
Conversation
Co-Authored-By: Kyu Choi <kyuhwanchoi0423@gmail.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Co-Authored-By: Kyu Choi <kyuhwanchoi0423@gmail.com>
Co-Authored-By: Kyu Choi <kyuhwanchoi0423@gmail.com>
Test Coverage Report
|
Co-Authored-By: Kyu Choi <kyuhwanchoi0423@gmail.com>
Co-Authored-By: Kyu Choi <kyuhwanchoi0423@gmail.com>
Test Coverage Report
|
Per-File Coverage Details
|
| } else { | ||
| body += `| **Base branch** (\`${{ github.base_ref }}\`) | _No JaCoCo configured_ |\n`; | ||
| } | ||
| body += `| **PR branch** (\`${{ github.head_ref }}\`) | ${prCoverage}% |\n`; |
There was a problem hiding this comment.
🔴 Script injection via github.head_ref in JavaScript template literal
On line 127, ${{ github.head_ref }} is interpolated directly into a JavaScript template literal (backtick string). Since github.head_ref is attacker-controlled (the PR author chooses their branch name), and Git branch names can contain $, {, and } characters, an attacker can craft a branch name like exploit${require('child_process').execSync('malicious command')} which would be evaluated as JavaScript code inside the template literal. The workflow has pull-requests: write permissions, so the attacker could use this to post arbitrary comments, exfiltrate the GITHUB_TOKEN, or read repository contents. The fix is to pass github.head_ref as an environment variable and reference it via process.env inside the script instead of using ${{ }} interpolation.
Vulnerable interpolation pattern
# Line 127 - github.head_ref injected into JS template literal
body += `| **PR branch** (\`${{ github.head_ref }}\`) | ${prCoverage}% |\n`;Should instead use:
env:
HEAD_REF: ${{ github.head_ref }}
BASE_REF: ${{ github.base_ref }}
script: |
const headRef = process.env.HEAD_REF;
...
body += `| **PR branch** (\`${headRef}\`) | ${prCoverage}% |\n`;Prompt for agents
In .github/workflows/coverage.yml, the actions/github-script step at line 96-157 directly interpolates github.head_ref and github.base_ref using ${{ }} syntax inside JavaScript template literals, which allows script injection. Fix by:
1. Add an `env` block to the step (after `with:` at line 97):
env:
HEAD_REF: ${{ github.head_ref }}
BASE_REF: ${{ github.base_ref }}
2. Inside the script block, replace all uses of ${{ github.head_ref }} with process.env.HEAD_REF and all uses of ${{ github.base_ref }} with process.env.BASE_REF.
Specifically, change lines 123, 125, and 127 from:
body += `| **Base branch** (\`${{ github.base_ref }}\`) | ${baseCoverage}% |\n`;
to:
body += `| **Base branch** (\`${process.env.BASE_REF}\`) | ${baseCoverage}% |\n`;
And line 127 from:
body += `| **PR branch** (\`${{ github.head_ref }}\`) | ${prCoverage}% |\n`;
to:
body += `| **PR branch** (\`${process.env.HEAD_REF}\`) | ${prCoverage}% |\n`;
Was this helpful? React with 👍 or 👎 to provide feedback.
| echo "PR branch line coverage: $pct% (missed=$missed, covered=$covered)" | ||
|
|
||
| - name: Checkout base branch | ||
| run: git checkout ${{ github.base_ref }} |
There was a problem hiding this comment.
🟡 Shell injection via unquoted github.base_ref in run: block
On line 58, ${{ github.base_ref }} is interpolated directly into a shell run: command without quotes: git checkout ${{ github.base_ref }}. While the workflow restricts to PRs targeting master (mitigating this somewhat), directly interpolating GitHub context values into shell commands is a security anti-pattern. If the workflow trigger was ever broadened to allow other base branches, a branch name containing shell metacharacters could execute arbitrary commands. The safe pattern is to use an environment variable: env: BASE_REF: ${{ github.base_ref }} and then git checkout "$BASE_REF".
Prompt for agents
In .github/workflows/coverage.yml line 57-58, change the step to use an environment variable instead of direct interpolation:
- name: Checkout base branch
run: git checkout "$BASE_REF"
env:
BASE_REF: ${{ github.base_ref }}
This prevents shell injection if the workflow trigger is ever broadened to allow base branches with special characters.
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
Adds the JaCoCo Gradle plugin to
build.gradleto enable test coverage reporting and enforcement, and adds a GitHub Actions workflow that posts before/after coverage percentages as a PR comment.build.gradlechangesjacocoplugin (toolVersion0.8.8)jacocoTestReportto generate HTML and XML reports after each test runjacocoTestCoverageVerificationwith a 34% line coverage minimum threshold (matches current actual coverage)test→jacocoTestReport(viafinalizedBy) andcheck→jacocoTestCoverageVerification(viadependsOn)testtask block (merginguseJUnitPlatform()into the new block; also fixes tab→space indentation).github/workflows/coverage.yml(new)masteractions/github-script@v6(with emoji diff indicator: 🟢 up / 🔴 down / ⚪ unchanged). Comment is upserted using a hidden HTML marker so re-runs update in place.madrapps/jacoco-report@v1.6.1Review & Testing Checklist for Human
counter type="LINE"from JaCoCo XML usinggrep -oP ... | tail -1. This relies on the last<counter type="LINE" .../>in the file being the report-level summary. Confirm this holds for the current JaCoCo version (0.8.8) and consider whether a more robust parser (e.g.xmllint) would be preferable.masterdoes not currently have JaCoCo, the first run of this workflow will show "No JaCoCo configured" for the base branch. After merging, open a trivial follow-up PR to confirm the workflow posts a proper before/after table with a numeric diff.min-coverage-changed-files: 60threshold in themadrapps/jacoco-reportstep. This requires 60% line coverage on files changed in a PR, which may be aggressive depending on which files are touched../gradlew jacocoTestCoverageVerificationpasses locally. The 34% threshold was set to match measured coverage — verify it still holds.Suggested test plan: Merge this PR, then open a small follow-up PR that touches a Java file. Confirm the workflow posts two comments: (1) a before/after coverage table with a numeric diff, and (2) per-file coverage details from
madrapps/jacoco-report.Notes
spotlessCheckfailure onmasteris not introduced by this PR.// TODOcomment inbuild.gradlenotes it should be raised over time.actions/checkout@v3,actions/setup-java@v3, andactions/cache@v3, which run on Node.js 20 (deprecated by GitHub starting June 2026). Consider upgrading to v4 in a follow-up.Requested by: @choikh0423
Link to Devin session