Skip to content

UN-4523 [MISC] Enforce the CLA with Zipstack/cla-action instead of cla-assistant.io - #2386

Merged
ritwik-g merged 3 commits into
mainfrom
UN-4523-cla-workflow
Oct 27, 2026
Merged

ritwik-g merged 3 commits into
mainfrom
UN-4523-cla-workflow

Conversation

@ritwik-g

@ritwik-g ritwik-g commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

What

Replaces cla-assistant.io with our own CLA check, Zipstack/cla-action. cla-assistant.io (hosted by SAP) has had repeated outages that leave the required license/cla check stuck and block PRs. Jira: UN-4523. Slack discussion: #C058WQU0R6E thread.

  • Same CLA text (the existing gist) and the same license/cla status context. The main ruleset requires that context without pinning it to the cla-assistant app, so no ruleset change is needed.
  • Contributors sign once by commenting I have read the CLA Document and I hereby sign the CLA on their PR.
  • Signatures live in the private Zipstack/cla-signatures repo. All 41 existing cla-assistant signers are imported, so nobody has to sign again.
  • Exempt: org members, bots, and commits made with Claude Code. Claude Code commits are attributed to the PR author, so the PR author is the one who signs for them.
  • The token for the signatures repo comes from the zipstack-cla GitHub App, which is installed on cla-signatures only. The org variable CLA_APP_CLIENT_ID and org secret CLA_APP_PRIVATE_KEY are visible to this repo only.
  • Both actions are pinned by commit SHA.

Security

The workflow runs on pull_request_target, so fork PRs get the app token. It is safe only because it never checks out or runs the PR's code; the comment at the top of the file says so.

Rollout

pull_request_target workflows run from the base branch, so this one can't run on its own PR. It starts working once merged.

  1. At merge: unlink Zipstack/unstract on cla-assistant.io, so the two tools don't both set license/cla.
  2. Live test: a PR from a fork by a non-member account. It should fail and prompt to sign; signing via comment should flip it to success.
  3. Open PRs whose license/cla is stuck can be re-checked by commenting recheck.

Testing

🤖 Generated with Claude Code

cla-assistant.io's outages leave the required license/cla check stuck and
block PRs. The new workflow sets the same license/cla status, so the
ruleset needs no change. Signatures (41 imported from cla-assistant.io)
live in the private Zipstack/cla-signatures repo. (UN-4523)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@ritwik-g ritwik-g left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Standardized review (review-deep) — self-review-loop round 1

Mode: INITIAL. Round tip c842015af94a534068c211a1be6c8867810322de; delta = merge base dd4ec29..tip (whole PR, 1 file, 43 lines). Review chosen: review-deep, because the PR is a pull_request_target workflow handling a secret-backed GitHub App token. That is an attack surface and an auth/secrets trigger, so review-shallow was not used.

Signals: lenses 4, 11 raised (.github/workflows/cla.yaml: pull_request_target, org secret, app token, CI workflow).

Verdict — APPROVE WITH NITS

Summary — Critical: 0 · High: 0 · Medium: 0 · Low: 3 · Lenses run: 19/19 (N/A ones are listed below with reasons)

Findings

Findings #1 and #2 are posted inline.

Unanchored findings

  • #3 [Low] [Lens 17 — Cross-document consistency] (tallied; anchored to a file outside the diff) README.md:15 still shows the cla-assistant.io badge and links to https://cla-assistant.io/Zipstack/unstract. Once the repo is unlinked there (rollout step 1), that badge points contributors at a dead signing flow. The file is not in this diff. A follow-up could remove the badge or point it at the CLA gist.

Lens checklist

  1. Intent & scope — Clean (code-reviewer)
  2. Correctness — Clean (code-reviewer: inputs, triggers, permissions and status context checked against the pinned action sources)
  3. Error handling — Clean (code-reviewer)
  4. Security — Clean (security deep-dive done, 0 findings; orchestrator also checked the app installation permissions (contents:write, members:read, selected repos), the secret and variable visibility (unstract only), and that there is no checkout)
  5. Data integrity & migrations — N/A — no data or schema in this repo is changed (signature writes go to a separate repo, through the action)
  6. Concurrency — Clean (orchestrator: the concurrency group is keyed per PR, a pending run is replaced, and every run re-collects all sign comments)
  7. API compatibility — N/A — no API, payload or wire format; the status context license/cla is unchanged
  8. Reliability & resilience — See findings #2
  9. Performance & cost — Clean (code-reviewer)
  10. Maintainability — Clean (code-reviewer)
  11. Operational safety — Clean (orchestrator: the rollout and the double-writer window with cla-assistant are documented in the PR body; a failed mint fails closed, with no status, so the required check blocks)
  12. LLM/agent — N/A — no prompts or model config
  13. Tests — N/A — no test suite exists for workflow files; actionlint and pre-commit pass locally (orchestrator ran both)
  14. Dependencies & build — Clean (orchestrator: both actions are pinned by commit SHA, and the SHAs match tags v3.3.0 and v1.0.0)
  15. Tooling/lint — Clean (orchestrator ran actionlint and pre-commit run --files .github/workflows/cla.yaml; both pass)
  16. Doc accuracy — See findings #1
  17. Cross-document consistency — See findings #3
  18. On-prem experience — N/A — CI workflow only; nothing ships to a customer cluster
  19. Scaling & workload lifecycle — N/A — no workloads

Boundary sweep: 1 sink traced (the token goes to the action input; the action masks it and never logs it), clean.
Security deep-dive: done

Open questions for the author — Does a Dependabot-triggered pull_request_target run receive Actions secrets (#2)?

Assumptions made — The cla-action behaviour at b68f7dd is as read from source. It was reviewed separately in Zipstack/cla-action#1 and is not re-reviewed here.

cost: agents=4 wall=03:33 lenses-examined=13 of 19 files=1 lines=43

Comment thread .github/workflows/cla.yaml
Comment thread .github/workflows/cla.yaml
…badge

Dependabot-triggered runs get no Actions secrets, so minting the
signatures token would fail and leave license/cla pending (as it already
is under cla-assistant on #2226 and #2190). Set the status directly for
those runs; anyone else's run on a Dependabot PR still goes through the
action. The README badge pointed at cla-assistant.io, which we unlink at
cutover; it now links to the CLA itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@ritwik-g ritwik-g left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Standardized review (review-deep) — self-review-loop round 2

Mode: FOLLOWUP. Round tip a0e0d3729a5e405ffe9eeda1f647a54bc284af1e; round delta c842015..a0e0d37 (cla.yaml: new "Pass Dependabot runs" step and actor conditions; README: CLA badge). Review chosen: review-deep, because the delta adds a step that writes the required license/cla status (an auth/merge-gate surface), so review-shallow was not used.

Verdict — BLOCK

Prior context fetched — followup-fetch.sh exited 0 (6 of 6). Previous-review SHA: c842015af94a534068c211a1be6c8867810322de.

Scope change — NO

Prior findings status

  • #1 [Low] Header says "every contributor" → WAIVED — the author chose to keep the wording (round-1 thread reply).
  • #2 [Low] Dependabot runs may lack Actions secrets → OBSOLETE — round 1's premise was unverified and is contradicted by GitHub's docs (see #5). The step added to address it introduces #4.
  • #3 [Low] README cla-assistant badge → RESOLVED in a0e0d37 — README.md:15 now links to the same CLA gist as cla-document-url.

New findings

Findings #4, #5 and #6 are posted inline.

Lens checklist

  1. Intent & scope — Clean (code-reviewer)
  2. Correctness — See findings #4
  3. Error handling — See findings #4
  4. Security — See findings #4
  5. Data integrity & migrations — N/A — no data or schema touched
  6. Concurrency — Clean (orchestrator: the new step does not change the concurrency group)
  7. API compatibility — N/A — no API; status context unchanged
  8. Reliability & resilience — Clean (orchestrator: Dependabot runs fall through to the action, which allowlists bots)
  9. Performance & cost — Clean (code-reviewer)
  10. Maintainability — Clean (code-reviewer)
  11. Operational safety — Clean (orchestrator)
  12. LLM/agent — N/A — no prompts or model config
  13. Tests — N/A — no test suite exists for workflow files; actionlint is run per commit
  14. Dependencies & build — Clean (orchestrator: the gh CLI is preinstalled on ubuntu-latest; no new actions)
  15. Tooling/lint — Clean (orchestrator: actionlint passes on a0e0d37)
  16. Doc accuracy — See findings #5, #6
  17. Cross-document consistency — Clean (orchestrator: README gist URL matches cla-document-url)
  18. On-prem experience — N/A — CI workflow only
  19. Scaling & workload lifecycle — N/A — no workloads

Boundary sweep: 1 sink traced (the status description is a static string), clean.
Security deep-dive: unavailable — the vendored-prompt scan was dispatched but had not returned when this round was posted. Lens 4 rests on the code-reviewer agent and the orchestrator's own read, which both found #4.

Open questions for the author — None outstanding. The author has decided to remove the Dependabot step entirely; the fix lands in the next commit.

Assumptions made — GitHub platform behaviour is as documented and as published by the cited research.

cost: agents=4 wall=04:30 lenses-examined=13 of 19 files=2 lines=63

Comment thread .github/workflows/cla.yaml Outdated
Comment thread .github/workflows/cla.yaml Outdated
Comment thread .github/workflows/cla.yaml Outdated
The step assumed Dependabot-triggered runs get only Dependabot secrets.
That is wrong for pull_request_target: GitHub restricts it only when the
PR's base ref was created by Dependabot. A normal Dependabot PR onto main
gets the Actions secrets, and the action exempts it as *[bot].

The step also opened a bypass. It keyed on github.actor, which an outside
contributor can make read dependabot[bot] by having Dependabot in their
fork re-push the PR branch (`@dependabot recreate`). That posted
license/cla=success on unsigned commits and skipped the real check.

The workflow steps go back to the earlier version. The README badge change
is kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@ritwik-g ritwik-g left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Standardized review (review-deep) — self-review-loop round 3

Mode: FOLLOWUP. Round tip 3738fe2401815308b116a5a7c82a87d449b0901c; round delta a0e0d37..3738fe2 (cla.yaml: removes the "Pass Dependabot runs" step and both github.actor skips, restoring the c842015 steps; README badge from a0e0d37 kept). Review chosen: review-deep, because the PR as a whole is a pull_request_target workflow that handles an org secret, so the shallow review's eligibility check excludes it.

Verdict — APPROVE

Prior context fetched — followup-fetch.sh exited 0 (6 of 6). Previous-review SHA: a0e0d3729a5e405ffe9eeda1f647a54bc284af1e.

Scope change — NO

Prior findings status

  • #4 [High] Spoofable github.actor gives a CLA bypass → RESOLVED in 3738fe2. The step and the skips are gone, and every run goes through the action. Confirmed independently by the code-reviewer and the security deep-dive.
  • #5 [Medium] False premise in the comment → RESOLVED in 3738fe2. The comment was removed with the step. The commit message's restatement (that GitHub restricts pull_request_target only when the PR's base ref was created by Dependabot) was checked against the GitHub docs by the code-reviewer.
  • #6 [Low] Re-run wording → OBSOLETE, because the comment was removed.
  • #1 WAIVED, #2 OBSOLETE, #3 RESOLVED, unchanged from round 2.

New findings — none. The code-reviewer reported none for lenses 1, 2, 3, 9, 10 and 15 and walked through each actor class: outside forks, org members, Dependabot, other bots, and bot issue_comment runs. The security deep-dive found no candidates.

Lens checklist

  1. Intent & scope — Clean (code-reviewer)
  2. Correctness — Clean (code-reviewer)
  3. Error handling — Clean (code-reviewer)
  4. Security — Clean (security deep-dive done with 0 findings; orchestrator confirmed no step sets license/cla outside the action)
  5. Data integrity & migrations — N/A — no data or schema touched
  6. Concurrency — Clean (orchestrator: concurrency group unchanged)
  7. API compatibility — N/A — no API; status context unchanged
  8. Reliability & resilience — Clean (orchestrator: Dependabot PRs onto main get secrets, and the action exempts them as bots)
  9. Performance & cost — Clean (code-reviewer)
  10. Maintainability — Clean (code-reviewer)
  11. Operational safety — Clean (orchestrator)
  12. LLM/agent — N/A — no prompts or model config
  13. Tests — N/A — no test suite exists for workflow files; actionlint passes at 3738fe2
  14. Dependencies & build — Clean (orchestrator: pins unchanged)
  15. Tooling/lint — Clean (orchestrator ran actionlint at 3738fe2, pass)
  16. Doc accuracy — Clean (orchestrator: the remaining comments are those cleared in round 1, apart from waived #1; commit message claims checked)
  17. Cross-document consistency — Clean (orchestrator: README gist matches cla-document-url)
  18. On-prem experience — N/A — CI workflow only
  19. Scaling & workload lifecycle — N/A — no workloads

Boundary sweep: 1 sink traced (the token goes to the action input), clean.
Security deep-dive: done

Open questions for the author — none.

Assumptions made — GitHub's Dependabot-on-Actions behaviour is as documented. A Dependabot PR after merge will confirm it.

cost: agents=2 wall=04:12 lenses-examined=13 of 19 files=2 lines=44

@ritwik-g
ritwik-g marked this pull request as ready for review October 27, 2026 09:44
@greptile-apps

greptile-apps Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

via Greptile

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable defect was established.

Summary

Replaces the CLA badge and adds a workflow that delegates CLA checks to the pinned Zipstack/cla-action.

  • Unstract pull requests use Zipstack's workflow to check CLA signatures.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[PR opened, updated, or reopened] --> C[CLA job]
  B[Comment on a PR] --> C
  C --> D[Create signatures repository token]
  D --> E[Run pinned CLA action]
  E --> F[Read or record signatures]
  E --> G[Set license/cla status]
Loading

Reviews (1) · Last reviewed commit: "ci: drop the Dependabot pass step from t..." · Reviewed by Greptile

@github-actions

Copy link
Copy Markdown
Contributor

Unstract test results

Per-group results

Status Group Tier Passed Failed Errors Skipped Duration (s)
⚪ e2e-agent-kv e2e 0 0 0 1 0.1
✅ e2e-api-deployment e2e 3 0 0 0 21.9
✅ e2e-coowners e2e 1 0 0 0 1.1
✅ e2e-etl e2e 1 0 0 0 14.2
✅ e2e-login e2e 2 0 0 0 1.0
✅ e2e-prompt-studio e2e 1 0 0 0 6.9
✅ e2e-smoke e2e 2 0 0 0 0.9
✅ e2e-workflow e2e 1 0 0 0 15.9
✅ frontend unit 629 0 0 0 10.7
✅ integration-backend integration 1437 0 0 26 78.9
✅ integration-connectors integration 1 0 0 7 9.0
✅ integration-workers integration 181 0 0 1 53.8
❌ ui e2e 0 1 0 0 0.0
✅ unit-agent-kv-schema unit 143 0 0 0 0.9
✅ unit-backend unit 2035 0 0 1 35.6
✅ unit-connectors unit 82 0 0 0 7.0
✅ unit-core unit 281 0 0 0 1.8
✅ unit-filesystem unit 1 0 0 0 2.5
✅ unit-platform-service unit 18 0 0 0 1.6
✅ unit-rig unit 120 0 0 0 2.7
✅ unit-runner unit 12 0 0 0 1.8
✅ unit-sdk1 unit 878 0 0 0 28.8
✅ unit-tool-sidecar unit 8 0 0 0 1.1
✅ unit-workers unit 1534 0 0 2 104.4
TOTAL 7371 1 0 38 402.5

Critical paths

⚠️ Critical paths not yet covered

  • workflow-execution-fan-out — Multi-file workflow execution fans out to file-processing workers and rejoins. (declared coverage: no groups declared)
✅ Covered critical paths
  • auth-login — covered by e2e-login
  • adapter-register-llm — covered by integration-backend
  • workflow-author — covered by integration-backend
  • co-owner-manage — covered by integration-backend, e2e-coowners
  • workflow-create-execute — covered by e2e-workflow
  • api-deployment-provision — covered by integration-backend
  • api-deployment-auth — covered by integration-backend
  • api-deployment-run — covered by e2e-api-deployment
  • mcp-server-auth — covered by integration-backend
  • mcp-platform-auth — covered by integration-backend
  • platform-key-whoami — covered by integration-backend
  • prompt-studio-author — covered by integration-backend
  • prompt-studio-fetch-response — covered by e2e-prompt-studio
  • connector-register-test — covered by integration-backend
  • pipeline-etl-execute — covered by e2e-etl
  • usage-aggregate-read — covered by integration-backend
  • usage-token-tracking — covered by e2e-api-deployment
  • callback-result-delivery — covered by e2e-api-deployment

@ritwik-g
ritwik-g merged commit 27ab86c into main Oct 27, 2026
26 checks passed
@ritwik-g
ritwik-g deleted the UN-4523-cla-workflow branch October 27, 2026 10:01
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.

1 participant