Skip to content

fix: require DAG write permission for inline specs#2250

Merged
yohamta0 merged 2 commits into
mainfrom
fix/operator-inline-spec-auth
Jun 2, 2026
Merged

fix: require DAG write permission for inline specs#2250
yohamta0 merged 2 commits into
mainfrom
fix/operator-inline-spec-auth

Conversation

@yohamta0

@yohamta0 yohamta0 commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Require DAG write permission for submitted DAG specs while preserving stored-DAG execution for operator credentials.

Changes

  • Preauthorize inline execute/enqueue requests from request labels or submitted spec metadata before materializing the inline DAG.
  • Preauthorize edit-retry requests before building the retry plan, including both the source run workspace and edited spec target workspace.
  • Keep source-run execute permission for normal retry paths and add regression coverage for operator API keys.

Related Issues

Addresses GHSA-3cmh-rvhh-32pq.

Acknowledgements

Thanks to Yaohui Wang (@YHalo-wyh) for responsibly reporting GHSA-3cmh-rvhh-32pq.

Testing

  • go test ./internal/service/frontend/api/v1 -run "TestOperatorCannotSubmit(InlineDAGSpec|EditedRetrySpec)" -count=1
  • go test ./internal/service/frontend/api/v1 -count=1
  • CI run 26802150562: all checks passed

Checklist

  • Code follows the project style guidelines
  • Self-review of the code has been performed
  • Tests have been added or updated as needed
  • Documentation has been updated as needed
  • Changes have been tested locally

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Permission model changes refactor DAG operation endpoints from requiring workspace "execute" permission to "DAG write" permission. Label extraction and authorization checks are moved earlier in execution and enqueue flows, and edit-retry permission checks are centralized via a new helper method. Authorization tests verify operator role constraints.

Changes

DAG Operation Permission Model Refactoring

Layer / File(s) Summary
Test helper for API key creation
internal/service/frontend/api/v1/apikeys_test.go
New createAPIKeyForRole helper authenticates as admin and creates API keys for specified roles, enabling subsequent role-based authorization tests.
DAG execution and enqueue permission updates
internal/service/frontend/api/v1/dagruns.go
ExecuteDAGRunFromSpec and EnqueueDAGRunFromSpec move label extraction and workspace authorization checks earlier in the request flow, changing from requireExecuteForWorkspace to requireDAGWriteForWorkspace for the runtime workspace.
Edit-retry permission centralization
internal/service/frontend/api/v1/dagruns_edit_retry.go
New requireEditRetryPermissions helper centralizes permission checks for edit-retry operations, requiring DAG-run status execution permission and DAG write permission for the edited workspace; PreviewEditRetryDAGRun and EditRetryDAGRun are refactored to use this helper with permission checks moved earlier before validation error returns.
Permission configuration and authorization tests
internal/service/frontend/api/v1/dagruns_edit_retry_internal_test.go, internal/service/frontend/api/v1/dagruns_test.go
Edit-retry test configuration is updated to include PermissionWriteDAGs; two new tests verify operator role cannot execute/enqueue inline DAG specs or preview/perform edit-retry operations, all returning HTTP 403 Forbidden.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request has no description provided by the author, missing all required template sections including Summary, Changes, Related Issues, and Checklist. Please add a comprehensive pull request description following the template with sections for Summary, Changes, Related Issues, and Checklist items.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: enforcing DAG write permission for inline specs, which is the core objective of this PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/operator-inline-spec-auth

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
internal/service/frontend/api/v1/dagruns_edit_retry.go (1)

73-80: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Move edit-retry authorization ahead of plan construction.

buildEditRetryPlan still loads and resolves the submitted spec before requireEditRetryPermissions runs, so a caller without DAG-write can drive the full edited-spec parse path and temp-file writes and only gets rejected at the end. If edit-retry specs are supposed to be gated by DAG-write, the workspace/auth decision needs to happen before building the plan.

Also applies to: 128-139

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/service/frontend/api/v1/dagruns_edit_retry.go` around lines 73 - 80,
Move the authorization check so it runs before any spec loading/plan
construction: call requireEditRetryPermissions earlier (before
buildEditRetryPlan) using the request identifiers (e.g., request.Name and
request.DagRunId or any lightweight workspace/DAG identifier) so callers without
DAG-write cannot trigger spec parsing or temp-file writes; to do this, adjust
requireEditRetryPermissions' usage (and if necessary its signature) to accept
the lightweight identifiers instead of a full plan, invoke it at the top of the
handler prior to calling buildEditRetryPlan, and apply the same change to the
second occurrence (the block that mirrors lines 128-139).
internal/service/frontend/api/v1/dagruns.go (1)

198-210: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Authorize before materializing the inline DAG.

Both paths still call loadInlineDAG before the new workspace write check, so an operator without DAG-write can force full spec parsing and temp-file creation and can still get pre-auth validation failures from that load path. That weakens the boundary this PR is trying to introduce; the workspace decision needs to come from a lightweight pre-parse/auth step before loadInlineDAG.

Also applies to: 280-292

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/service/frontend/api/v1/dagruns.go` around lines 198 - 210, Move the
workspace-write authorization before materializing the inline DAG: call
extractLabelsParam(request.Body.Labels, request.Body.Tags) first, then derive
the workspace with a lightweight helper (e.g., implement
runtimeWorkspaceNameFromNameOrSpec(labels, request.Body.Name) or similar) that
does not call loadInlineDAG or fully parse the spec, and then call
requireDAGWriteForWorkspace(ctx, workspace). Only after that, call
loadInlineDAG(ctx, request.Body.Spec, request.Body.Name, dagRunId) and defer
cleanup; apply this change to both code paths (the block around loadInlineDAG at
the shown lines and the similar block at lines 280-292).
🧹 Nitpick comments (1)
internal/service/frontend/api/v1/dagruns_test.go (1)

425-479: ⚡ Quick win

Assert the operator still has source-run execute permission in this test.

This only proves preview/edit-retry return 403. It would still pass if requireDAGRunStatusExecute also started rejecting operators, which is exactly the contract this PR says must be preserved. Add one operator-allowed action on the stored run first (for example a normal retry) so the test pins the failure specifically to the new DAG-write gate on edited specs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/service/frontend/api/v1/dagruns_test.go` around lines 425 - 479, Add
an explicit check that the operatorKey can still perform an allowed action on
the original run before asserting preview/edit-retry are forbidden: inside
TestOperatorCannotSubmitEditedRetrySpec, after startBody.DagRunId is available
(use dagName and startBody.DagRunId), send a normal operator retry request (POST
to /api/v1/dag-runs/{dagName}/{startBody.DagRunId}/retry) with
WithBearerToken(operatorKey) and require a successful status (e.g.,
http.StatusOK) to prove execute permission is intact, then proceed to assert
that the preview (/edit-retry/preview) and edit (/edit-retry) requests return
http.StatusForbidden as already written.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@internal/service/frontend/api/v1/dagruns_edit_retry.go`:
- Around line 73-80: Move the authorization check so it runs before any spec
loading/plan construction: call requireEditRetryPermissions earlier (before
buildEditRetryPlan) using the request identifiers (e.g., request.Name and
request.DagRunId or any lightweight workspace/DAG identifier) so callers without
DAG-write cannot trigger spec parsing or temp-file writes; to do this, adjust
requireEditRetryPermissions' usage (and if necessary its signature) to accept
the lightweight identifiers instead of a full plan, invoke it at the top of the
handler prior to calling buildEditRetryPlan, and apply the same change to the
second occurrence (the block that mirrors lines 128-139).

In `@internal/service/frontend/api/v1/dagruns.go`:
- Around line 198-210: Move the workspace-write authorization before
materializing the inline DAG: call extractLabelsParam(request.Body.Labels,
request.Body.Tags) first, then derive the workspace with a lightweight helper
(e.g., implement runtimeWorkspaceNameFromNameOrSpec(labels, request.Body.Name)
or similar) that does not call loadInlineDAG or fully parse the spec, and then
call requireDAGWriteForWorkspace(ctx, workspace). Only after that, call
loadInlineDAG(ctx, request.Body.Spec, request.Body.Name, dagRunId) and defer
cleanup; apply this change to both code paths (the block around loadInlineDAG at
the shown lines and the similar block at lines 280-292).

---

Nitpick comments:
In `@internal/service/frontend/api/v1/dagruns_test.go`:
- Around line 425-479: Add an explicit check that the operatorKey can still
perform an allowed action on the original run before asserting
preview/edit-retry are forbidden: inside
TestOperatorCannotSubmitEditedRetrySpec, after startBody.DagRunId is available
(use dagName and startBody.DagRunId), send a normal operator retry request (POST
to /api/v1/dag-runs/{dagName}/{startBody.DagRunId}/retry) with
WithBearerToken(operatorKey) and require a successful status (e.g.,
http.StatusOK) to prove execute permission is intact, then proceed to assert
that the preview (/edit-retry/preview) and edit (/edit-retry) requests return
http.StatusForbidden as already written.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 86af08c6-96b4-43aa-a88d-7f3ce9060595

📥 Commits

Reviewing files that changed from the base of the PR and between 2b240b8 and 700e0e9.

📒 Files selected for processing (5)
  • internal/service/frontend/api/v1/apikeys_test.go
  • internal/service/frontend/api/v1/dagruns.go
  • internal/service/frontend/api/v1/dagruns_edit_retry.go
  • internal/service/frontend/api/v1/dagruns_edit_retry_internal_test.go
  • internal/service/frontend/api/v1/dagruns_test.go

@yohamta0 yohamta0 merged commit 80611df into main Jun 2, 2026
11 checks passed
@yohamta0 yohamta0 deleted the fix/operator-inline-spec-auth branch June 2, 2026 07:57
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