Skip to content

fix(skit): add missing oneshot + plugin allowlist checks to batch API#294

Merged
streamer45 merged 6 commits intomainfrom
devin/1776006535-fix-batch-validation
Apr 13, 2026
Merged

fix(skit): add missing oneshot + plugin allowlist checks to batch API#294
streamer45 merged 6 commits intomainfrom
devin/1776006535-fix-batch-validation

Conversation

@staging-devin-ai-integration
Copy link
Copy Markdown
Contributor

@staging-devin-ai-integration staging-devin-ai-integration bot commented Apr 12, 2026

Summary

Both handle_validate_batch and handle_apply_batch were missing two validation checks that handle_add_node enforces:

  1. Oneshot-only marker node rejection: Nodes with kind streamkit::http_input or streamkit::http_output must be rejected on the dynamic control plane (they are only valid in oneshot HTTP pipelines).

  2. Plugin allowlist check: Nodes with kind starting with plugin:: must be checked against perms.is_plugin_allowed().

Additionally, handle_validate_batch had several gaps compared to handle_apply_batch:

  • No session existence or ownership check (session_id was discarded)
  • No duplicate node_id pre-validation
  • Returned opaque Error { message } instead of structured ValidationResult { errors }

Changes:

  • Extracted validate_add_node_op() as the single source of truth for AddNode validation. All three call sites (handle_add_node, handle_validate_batch, handle_apply_batch) now use this shared function.
  • Added session existence + ownership checks to handle_validate_batch, mirroring handle_apply_batch.
  • Added duplicate node_id pre-validation to handle_validate_batch, mirroring handle_apply_batch's Add/Remove simulation.
  • Changed handle_validate_batch to return structured ValidationResult { errors } with ValidationError entries (including node_id context) instead of opaque Error { message }. All errors are now collected and reported at once instead of failing on the first. Session-not-found and ownership-denied remain as pre-flight Error responses.
  • Added 16 integration tests covering: oneshot rejection, plugin allowlist rejection (with restrictive config), duplicate node_id detection, multi-error reporting, session-not-found, mixed batches, cross-role ownership rejection, and valid node passthrough — for both ValidateBatch and ApplyBatch.

Closes #287

Review & Testing Checklist for Human

  • Verify validate_add_node_op() correctly consolidates all per-node checks (oneshot, node allowlist, plugin allowlist, file_reader, file_writer, script_path)
  • Verify duplicate node_id pre-validation in handle_validate_batch mirrors handle_apply_batch
  • Verify the ValidationResult wire format change is acceptable for clients (previously Error, now ValidationResult with structured errors for operation-level failures)
  • Test manually via WebSocket: send ValidateBatch with two invalid nodes and confirm both errors are returned

Notes

  • The ValidationResult response change is intentional: the success path already returned ValidationResult { errors: [] }, so the failure path now consistently returns the same shape with populated errors.
  • Pre-flight checks (session-not-found, ownership-denied) still return Error since they prevent any validation from running.
  • Cross-role ownership tests use a role_header config + custom WS headers to simulate multi-role access.

Link to Devin session: https://staging.itsdev.in/sessions/d053d3e7034c445eb190ae2b9193974e
Requested by: @streamer45


Staging: Open in Devin

Both handle_validate_batch and handle_apply_batch were missing two
validation checks that handle_add_node enforces:

1. Oneshot-only marker node rejection: nodes with kind
   streamkit::http_input or streamkit::http_output must be rejected
   on the dynamic control plane.

2. Plugin allowlist check: nodes with kind starting with plugin::
   must be checked against perms.is_plugin_allowed().

Add both checks to the batch handlers, mirroring the exact same
error messages and logic from handle_add_node for consistency.

Add integration tests verifying the batch API correctly rejects
oneshot-only nodes and allows valid node types.

Closes #287

Signed-off-by: StreamKit Devin <devin@streamkit.dev>
Co-Authored-By: Claudio Costa <cstcld91@gmail.com>
@staging-devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

staging-devin-ai-integration[bot]

This comment was marked as resolved.

streamkit-devin and others added 3 commits April 12, 2026 15:34
handle_validate_batch was ignoring the session_id parameter, meaning
it performed no session existence or ownership check unlike
handle_apply_batch. This allowed users to validate operations against
sessions they don't own.

Add session lookup and can_access_session check to
handle_validate_batch, mirroring handle_apply_batch's authorization
semantics.

Signed-off-by: StreamKit Devin <devin@streamkit.dev>
Co-Authored-By: Claudio Costa <cstcld91@gmail.com>
Address review feedback:

1. Extract validate_add_node_op() as the single source of truth for
   AddNode validation (oneshot check, node allowlist, plugin allowlist,
   file path checks). All three call sites (handle_add_node,
   handle_validate_batch, handle_apply_batch) now use this shared
   function instead of duplicated logic.

2. Fix test_validate_batch_rejects_disallowed_plugin to actually test
   rejection by configuring a server with an empty plugin allowlist.

3. Add test_validate_batch_rejects_nonexistent_session covering the
   session existence check added in the previous commit.

4. Add ApplyBatch tests for mixed-batch and plugin rejection parity
   with ValidateBatch tests.

5. Add type aliases (WsWriter, WsReader) to reduce verbose WebSocket
   type signatures in the test file.

Signed-off-by: StreamKit Devin <devin@streamkit.dev>
Co-Authored-By: Claudio Costa <cstcld91@gmail.com>
…ult, and ownership tests

- Add duplicate node_id pre-validation to handle_validate_batch, mirroring
  the simulation in handle_apply_batch (Add/Remove sequence).
- Return structured ValidationResult with ValidationError entries instead
  of opaque ResponsePayload::Error for validation failures. All errors are
  now collected and reported at once instead of failing on the first.
- Session-not-found and ownership-denied remain as pre-flight Error
  responses (they prevent any validation from running).
- Add 5 new integration tests:
  - test_validate_batch_rejects_duplicate_node_id
  - test_validate_batch_reports_all_errors
  - test_validate_batch_rejects_cross_role_ownership
  - test_apply_batch_rejects_nonexistent_session
  - test_apply_batch_rejects_cross_role_ownership
- Update existing ValidateBatch tests to expect ValidationResult with
  structured error entries including node_id context.

Signed-off-by: StreamKit Devin <devin@streamkit.dev>
Co-Authored-By: Claudio Costa <cstcld91@gmail.com>
Copy link
Copy Markdown
Contributor Author

@staging-devin-ai-integration staging-devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 5 additional findings in Devin Review.

Staging: Open in Devin
Debug

Playground

Comment on lines +1196 to +1217
// Validate all AddNode operations against permission and security rules.
for op in operations {
if let streamkit_api::BatchOperation::AddNode { node_id, kind, params, .. } = op {
if let Some(message) =
validate_add_node_op(kind, params.as_ref(), perms, &app_state.config.security)
{
errors.push(ValidationError {
error_type: ValidationErrorType::Error,
message,
node_id: Some(node_id.clone()),
connection_id: None,
});
}
}
}

info!(operation_count = operations.len(), "Validated batch operations");
ResponsePayload::ValidationResult { errors: Vec::new() }
info!(
operation_count = operations.len(),
error_count = errors.len(),
"Validated batch operations"
);
ResponsePayload::ValidationResult { errors }
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.

🚩 Behavioral change: ValidateBatch now returns ValidationResult instead of Error for invalid operations

The old handle_validate_batch short-circuited on the first failing AddNode operation and returned ResponsePayload::Error { message }. The new code accumulates all errors into a Vec<ValidationError> and returns ResponsePayload::ValidationResult { errors } with a non-empty list. This changes the wire-level response shape from {"action": "error", "message": "..."} to {"action": "validationresult", "errors": [...]} for operation-level failures. Clients that were matching on the error action for validation failures would need updating. This is arguably the correct behavior for a validation endpoint (returning structured results), but it is a breaking change in the API contract.

Staging: Open in Devin

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

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.

Acknowledged — this is an intentional behavioral change requested in code review. The reviewer asked for ValidateBatch to return structured ValidationResult { errors } instead of opaque Error { message }, so that (a) the response shape is consistent between success and failure paths, and (b) all errors are reported at once instead of failing on the first.

Pre-flight checks (session-not-found, ownership-denied, permission-denied) still return ResponsePayload::Error since they prevent any validation from running.

No existing clients consume ValidateBatch error responses today (the batch API is new), so the migration risk is minimal.

streamkit-devin and others added 2 commits April 12, 2026 20:14
Signed-off-by: StreamKit Devin <devin@streamkit.dev>
Co-Authored-By: Claudio Costa <cstcld91@gmail.com>
@streamer45 streamer45 enabled auto-merge (squash) April 13, 2026 19:14
@streamer45 streamer45 merged commit 1bfeb7f into main Apr 13, 2026
13 checks passed
@streamer45 streamer45 deleted the devin/1776006535-fix-batch-validation branch April 13, 2026 19:24
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.

Batch API missing oneshot + plugin allowlist validation checks

2 participants