-
Notifications
You must be signed in to change notification settings - Fork 2
Add the Manual sol verify caller workflow #570
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
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| name: Manual sol verify | ||
| # Explorer source verification for a suite that is ALREADY on chain, run by | ||
| # hand. | ||
| # | ||
| # `manual-sol-artifacts.yaml` submits source only for what its own run | ||
| # broadcast, and the broadcast is idempotent: a rerun against networks that | ||
| # already hold the code broadcasts nothing, so there is nothing for `--verify` | ||
| # to submit and the run is green having verified nothing. A deploy that landed | ||
| # and then went unverified — a bad explorer key, a rate limit, an explorer that | ||
| # was down, or `verify: false` because the retry loop would have outlasted the | ||
| # deploy — is repaired here rather than by re-dispatching the deploy. | ||
| # | ||
| # Deliberately `workflow_dispatch` only, like the deploy. Unlike the deploy this | ||
| # never broadcasts and never reads `DEPLOYMENT_KEY`: `forge verify-contract` | ||
| # talks to the explorer API and nothing else, so it is safe to re-run and is | ||
| # already a no-op ("already verified") against an explorer that has the source. | ||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| contract: | ||
| type: string | ||
| required: true | ||
| description: | | ||
| Artifact path of the contract to submit, `path:Contract`. Paired with | ||
| `address` on the `manual verification command:` line | ||
| `script/Deploy.sol` prints for every network, whether it deployed | ||
| there or skipped it, so a run of the deploy is where both values come | ||
| from. They are also the `artifactPath` and the generated | ||
| `DEPLOYED_ADDRESS` of the five suites in | ||
| `src/abstract/RainlangDeploySuites.sol`. | ||
| address: | ||
| type: string | ||
| required: true | ||
| description: | | ||
| The deployed address. One value for every network, because the Zoltu | ||
| factory derives one address from the creation code. | ||
| networks: | ||
| type: string | ||
| required: true | ||
| default: arbitrum base base-sepolia mainnet flare hyperliquid polygon | ||
| description: | | ||
| Which explorers to submit to. FOUNDRY's chain names, not the | ||
| `[rpc_endpoints]` aliases, and the two differ on three of the seven: | ||
| the aliases `base_sepolia`, `ethereum` and `hyperevm` are rejected | ||
| outright, and the chain names are `base-sepolia`, `mainnet` and | ||
| `hyperliquid`. The `manual verification command:` line is NOT a source | ||
| for this field — it prints `--chain` with the alias it broadcast | ||
| under, which is the spelling that gets rejected here. The default is | ||
| `LibRainDeploy.supportedNetworks()` spelled the working way, i.e. | ||
| every network `script/Deploy.sol` broadcasts to, so it has to move | ||
| when that does. | ||
| jobs: | ||
| verify: | ||
| uses: rainlanguage/rainix/.github/workflows/rainix-manual-sol-verify.yaml@main | ||
| with: | ||
| contract: ${{ inputs.contract }} | ||
| address: ${{ inputs.address }} | ||
| networks: ${{ inputs.networks }} | ||
| secrets: inherit | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
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.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: rainlanguage/rainlang
Length of output: 13641
🏁 Script executed:
Repository: rainlanguage/rainlang
Length of output: 5161
🌐 Web query:
GitHub Actions reusable workflows caller permissions GITHUB_TOKEN nested workflows contents read checkout official documentation💡 Result:
In GitHub Actions, GITHUB_TOKEN permissions in reusable and nested workflows follow strict hierarchical rules to ensure security and prevent privilege escalation [1][2][3]. Permission Inheritance and Constraints: - Caller Control: When a caller workflow invokes a reusable workflow, it can specify permissions using the jobs.<job_id>.permissions key [1][3]. If not specified in the calling job, the called workflow inherits the default permissions for the GITHUB_TOKEN [1][2]. - Downgrade Only: GITHUB_TOKEN permissions passed from a caller workflow can be downgraded (made more restrictive) but never elevated (made more permissive) by the called workflow [1][3]. - Nested Workflows: For nested workflows (e.g., A > B > C), permissions can only be maintained or further restricted at each subsequent level [4][5][3]. If workflow A has contents: read, then workflows B and C cannot have contents: write [1][2]. All workflows in the chain must be accessible to the initial caller workflow [4][2]. - Execution Context: A called workflow is automatically granted access to the GITHUB_TOKEN, but its effective permissions are bound by the constraints established by the caller [2][3]. Best Practices: - Least Privilege: As a security best practice, explicitly define the minimum required permissions in your workflow or job level to limit the scope of the GITHUB_TOKEN [6]. - Nested Access: Remember that nested reusable workflows require all workflows in the chain to be accessible to the top-level caller [4][2]. If a workflow in the chain lacks necessary permissions or accessibility, the entire job will fail [1][2]. For further details on specific permission configurations, refer to the official GitHub documentation on Workflow syntax for GitHub Actions and Reusing workflow configurations [1][4][3].
Citations:
Restrict the caller's
GITHUB_TOKENtocontents: read.The called workflow runs the setup action with repository checkout enabled. Add a workflow- or job-level
permissionsblock. Without it, the called workflow inherits repository or organization defaults, which may grant write access.🧰 Tools
🪛 zizmor (1.29.0)
[warning] 53-60: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
[warning] 54-54: secrets unconditionally inherited by called workflow (secrets-inherit): this reusable workflow
(secrets-inherit)
🤖 Prompt for AI Agents
Sources: MCP tools, Linters/SAST tools
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.
Checked rather than assumed:
gh api repos/rainlanguage/rainlang/actions/permissions/workflowreportsdefault_workflow_permissions: write. So this is not spurious — with no block the called workflow really does get a write-scopedGITHUB_TOKEN, andcontents: readreally is all it needs (checkout, nix,forge verify-contract; the Actions cache usesACTIONS_RUNTIME_TOKEN, not this one).Not taking it in this PR, and not because it is wrong.
The cap belongs in
rainix-manual-sol-verify.yamlitself. A called workflow's own job-levelpermissionsapplies, and a caller can only downgrade from there, so one block upstream covers this repo, the other caller in rainlang,rain.deploy, and every future caller — instead of two of them. Put it in this one caller and the result ismanual-sol-artifacts.yaml, which is the workflow that actually reachesDEPLOYMENT_KEYand the RPCs, sitting unrestricted directly beside a hardened workflow that never broadcasts anything. That is the wrong half to harden first, and the inconsistency would read as deliberate to whoever finds it next.Flagged for upstream rather than dropped.
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.
@thedavidmeister, agreed. The reusablerainix-manual-sol-verify.yamlworkflow is the correct place forpermissions: contents: read.That change protects all current and future callers. The current PR does not include the upstream change, so I will keep this finding open.
Would you like me to create a GitHub issue for the Rainix follow-up?
🐇
✏️ Learnings added
You are interacting with an AI system.
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.
Filed as rainlanguage/rainix#361 — in rainix rather than here, since that is where the fix lands and where it covers
rain.deployand both callers at once.Two things surfaced while writing it up that make the finding stronger than the version above:
rainix-vercel.yaml:69(contents: read+pull-requests: write),rainix-tag-release.yaml:163(contents: write) andrainix-autopublish.yaml:72(id-token: write+contents: write) all declare job-level permissions sized to what their steps do.rainix-manual-sol-verify.yamlsimply does not have one, and it is the reusable in that set with the smallest genuine need. So this is a gap in a convention, not a new convention.contents: readreally is sufficient: the composite does a checkout, the Actions cache authenticates withACTIONS_RUNTIME_TOKENrather thanGITHUB_TOKEN, and the verify step reaches explorer APIs with theCI_DEPLOY_*keys. Nothing in the chain writes with the token.Keeping the thread open here is the right call — this caller is write-scoped until that lands.
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.
@thedavidmeister, thanks for the detailed validation.rainlanguage/rainix#361is the correct follow-up location. The existing Rainix workflow convention and the token-use audit both support a job-levelpermissions: contents: readblock inrainix-manual-sol-verify.yaml.This caller remains write-scoped until that upstream change lands. Keeping this thread open is correct.
🐇 🔍
🧠 Learnings used
You are interacting with an AI system.