Skip to content

feat(worker): fail closed on virtual_machine execution class without kata#199

Open
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1784590213-vm-class-fail-closed
Open

feat(worker): fail closed on virtual_machine execution class without kata#199
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1784590213-vm-class-fail-closed

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jul 20, 2026

Copy link
Copy Markdown

Summary

Closes a fail-closed gap in the VM-class isolation contract (SW-2). The worker already advertises the VirtualMachine capability only for IsolationProfile::Kata + a nonempty RuntimeClass, and placement matches jobs to workers by required_execution_class. But the provider itself never re-checked execution_class at provision time — only the APEX/gVisor case was guarded in validate_runtime_profile. So a virtual_machine spec reaching a mis-scoped/development provider would render and provision instead of being rejected.

This adds the missing provider-boundary check, mirroring the existing APEX one:

// in KubernetesDryRunProvider::validate_runtime_profile (shared by dry-run,
// apply, and fork paths via self.dry_run.validate_runtime_profile)
if spec.execution_class == ExecutionClass::VirtualMachine {
    ensure!(
        self.isolation_profile == IsolationProfile::Kata
            && self.runtime_class_name.as_deref().is_some_and(|n| !n.trim().is_empty()),
        "virtual_machine execution_class requires the kata isolation profile and a RuntimeClass"
    );
}

Defense-in-depth: VM-class (hostile-workload) provisioning now fails closed at the provider boundary rather than relying solely on placement-time capability matching.

Behavior unchanged for development_container / sandboxed_container and for correctly-configured Kata workers.

Test

New provider::tests::virtual_machine_execution_class_requires_kata_and_runtime_class:

  • Kata + kata-qemu RuntimeClass → provisions, pod carries runtimeClassName: kata-qemu.
  • development isolation → rejected.
  • Kata without RuntimeClass → rejected (fail closed).

Ran locally with the pinned toolchain (cargo +1.95.0):

  • cargo fmt --check — clean
  • cargo clippy -p sandboxwich-worker --all-targets -- -D warnings — clean
  • cargo test --workspace — all pass (163 worker tests incl. the new one). Postgres-backed contract tests were not run (SANDBOXWICH_TEST_POSTGRES_URL unset); they self-skip.

Link to Devin session: https://app.devin.ai/sessions/794b5e0853624962b67878f01835857c
Requested by: @haasonsaas


Open in Devin Review

…kata

The Kubernetes provider advertised the VirtualMachine worker capability only
for the kata isolation profile with a RuntimeClass, but validate_runtime_profile
never re-checked execution_class at provision time. Add a provider-boundary
check mirroring the APEX/gVisor one so a virtual_machine spec on a
non-kata / RuntimeClass-less worker is rejected rather than provisioned,
completing the fail-closed VM-class profile (SW-2).

Co-Authored-By: Jonathan Haas <jonathan@0xHaas.com>
@haasonsaas haasonsaas self-assigned this Jul 20, 2026
@devin-ai-integration

Copy link
Copy Markdown
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, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

Open in Devin Review

Comment on lines +620 to +629
if spec.execution_class == ExecutionClass::VirtualMachine {
anyhow::ensure!(
self.isolation_profile == IsolationProfile::Kata
&& self
.runtime_class_name
.as_deref()
.is_some_and(|name| !name.trim().is_empty()),
"virtual_machine execution_class requires the kata isolation profile and a RuntimeClass"
);
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

📝 Info: VM-class provider check consistent with capability advertisement

The new fail-closed check at crates/sandboxwich-worker/src/provider.rs:620-629 requires IsolationProfile::Kata plus a nonempty runtime_class_name for ExecutionClass::VirtualMachine. This is the exact inverse of the capability advertisement at crates/sandboxwich-worker/src/provider.rs:4877-4887, which pushes WorkerCapability::VirtualMachine only when runtime_class_name.is_some() and the profile is Kata. Since with_runtime_class_name already normalizes empty/whitespace strings to None, the is_some_and(|name| !name.trim().is_empty()) guard is equivalent to the advertisement's is_some() check, so the provider boundary and placement-time matching stay in agreement. validate_runtime_profile is invoked on all provision/fork/apply paths (provider.rs:939, provider.rs:3643, provider.rs:3687, provider.rs:5036), so the check is not bypassable.

Open in Devin Review

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks — agreed, that's the intent: the provider-boundary check is the deliberate inverse of the capability advertisement, and since with_runtime_class_name normalizes empty/whitespace to None, is_some_and(|n| !n.trim().is_empty()) is equivalent to the advertisement's is_some(). The trim().is_empty() guard is defensive against a future path that sets runtime_class_name without going through the normalizing setter. No change needed.

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