feat(ci): add comment on pr workflow by riteshfyi · Pull Request #663 · webex/widgets

Code Review

Great feature idea — the UX shown in the test comment (riteshfyi#5) looks clean and helpful for contributors. A few issues need to be addressed before this is production-ready, ranging from a security concern to reliability gaps.


🔴 High — Script injection via template literal in github-script

In the Post Release Comment on PR step:

const raw = `${{ needs.publish-npm.outputs.package_versions }}`;

GitHub Actions interpolates ${{ ... }} expressions before the script is executed, so the value of package_versions is injected verbatim into the JavaScript source. If yarn package-tools increment ever emits a backtick or a ${...} sequence (e.g., in a package name or log line), it breaks out of the template literal. GitHub's own security hardening guide recommends passing untrusted step outputs through intermediate environment variables:

- name: Post Release Comment on PR
  if: steps.get-pr.outputs.pr_number != ''
  env:
    PACKAGE_VERSIONS: ${{ needs.publish-npm.outputs.package_versions }}
    PR_NUMBER: ${{ steps.get-pr.outputs.pr_number }}
  uses: actions/github-script@v7
  with:
    script: |
      const prNumber = parseInt(process.env.PR_NUMBER);
      const raw = process.env.PACKAGE_VERSIONS;
      // rest of logic unchanged

The same pattern applies to '${{ steps.get-pr.outputs.pr_number }}' on the parseInt line.


🟡 Medium — Release tag URLs will 404

The comment generates links like:

`[\`${tag}\`](${repoUrl}/releases/tag/${tag})`

The publish-tag job only creates and pushes annotated Git tags — it does not create GitHub Release objects (no gh release create step). GitHub's releases/tag/<tag> URL returns a 404 (or a sparse stub) for tags that have no associated Release. Either:

  • Change the links to point to the tag tree: `${repoUrl}/tree/${tag}`, or
  • Add a gh release create "$TAG_NAME" --generate-notes call in publish-tag after each successful tag push.

This was also flagged by the automated Codex review.


🟡 Medium — Fragile bash pipe for capturing package_versions

echo "$OUTPUT" | while IFS= read -r line; do
  if echo "$line" | grep -q ' => '; then
    echo "__webex_release__: $line"
  fi
done >> $GITHUB_OUTPUT

This relies on the undocumented output format of yarn package-tools increment. Any change to that tool's output (e.g. a new progress or debug line containing =>) would silently corrupt the parsed versions. Additionally, without set -o pipefail, a failure inside the pipe won't fail the step. Prefer having package-tools emit structured JSON, or at minimum add set -euo pipefail at the top of the run block.


🟡 Medium — Version extraction in publish-tag is undocumented and fragile

VERSION=$(yarn package-tools sync --tag ${GITHUB_REF##*/} --packages "$TAGGABLE_PKG" | awk '{print $3}' | tr -d '%' | head -1)

The $3 column and % stripping are tied to a specific output format of yarn package-tools sync that is not documented here. If the tool output changes, tags will silently be created with empty or wrong version strings. Reading the version directly from package.json after the sync step would be more reliable:

VERSION=$(node -p "require('./packages/path-to-pkg/package.json').version")

🟡 Medium — Unquoted expression expansion in run: steps

Several existing run: steps embed ${{ ... }} expressions directly in shell without quotes:

OUTPUT=$(yarn package-tools increment --packages ${{ needs.analyze-changes.outputs.node-recursive }} --tag ${GITHUB_REF##*/})

The node-recursive output is a JSON array. While the source is internal, direct expression injection into shells is a pattern GitHub flags as risky. Pass the value through an env: variable at the step level and reference $PACKAGES in the shell script.


🟢 Low — comment-on-pr runs even when nothing was published

The job condition is:

needs: [publish-npm, publish-tag]

but there's no if: guard checking needs.publish-npm.outputs.published == 'true'. The script handles the no-packages case gracefully, but it still queries GitHub's API on every push. Adding a job-level condition avoids unnecessary API calls.


🟢 Low — Changelog stable_version stripping is too broad

const stableVersion = ver.replace(/-.*$/, '');

This converts 1.28.0-next.331.28.0, which is correct for next pre-releases. But it would also turn 2.0.0-rc.02.0.0 or 1.28.0-alpha.11.28.0, which may point to the wrong changelog entry. Consider making this logic explicit for the known release tracks, or parameterizing it.


🟢 Low — Comment deduplication relies on mutable message text

comment.body.includes('Your changes are now available')

If the wording ever changes, old "merged" comments won't be upgraded and new "available" comments won't be deduplicated. A more robust sentinel: add an invisible HTML comment at the top of the body, e.g. <!-- webex-release-bot-v1 -->, and match on that.


🔵 Nit — PR description placeholder not filled in

The description still contains < DESCRIBE YOUR CHANGES >. Please replace it with a brief explanation of what the workflow does (e.g., "Adds a comment-on-pr job that posts a release summary on the merged PR, listing updated package versions and a link to the changelog.").


Summary

Severity Issue
🔴 High Script injection via template literal in github-script
🟡 Medium Release tag URLs will 404 (no GitHub Releases created)
🟡 Medium Fragile bash pipe for package_versions output
🟡 Medium Fragile version extraction in publish-tag
🟡 Medium Unquoted expression expansion in run: steps
🟢 Low comment-on-pr runs even when nothing was published
🟢 Low Changelog stable_version stripping too broad
🟢 Low Comment deduplication relies on mutable message text
🔵 Nit PR description placeholder not filled in