Skip to content

feat(sdk): out of box controls phase 2#247

Open
namrataghadi-galileo wants to merge 6 commits into
feature/67101-out-of-box-controlsfrom
feature/67101-out-of-box-controls-phase-2
Open

feat(sdk): out of box controls phase 2#247
namrataghadi-galileo wants to merge 6 commits into
feature/67101-out-of-box-controlsfrom
feature/67101-out-of-box-controls-phase-2

Conversation

@namrataghadi-galileo

Copy link
Copy Markdown
Contributor

Summary

  • Added the Phase 2 static out-of-box control catalog so new installs seed useful non-Luna controls at startup.
  • Included regex PII/shell controls, JSON approval controls, and list-based RBAC/tool allowlist controls using the Phase 1 idempotent bootstrap path.

Scope

  • User-facing/API changes: No API contract changes. Seeded controls will appear in the existing Controls tab/list after startup.
  • Internal changes: Populated OUT_OF_BOX_CONTROL_TEMPLATES and expanded bootstrap/evaluator behavior tests.
  • Out of scope: Phase 3 Luna scorer metadata lookup and org-scoped Luna control generation.

Risk and Rollout

  • Risk level: low
  • Rollback plan: Revert the Phase 2 catalog additions in server/src/agent_control_server/bootstrap/out_of_box_controls.py, or remove specific templates from OUT_OF_BOX_CONTROL_TEMPLATES.

Testing

  • Added or updated automated tests
  • Ran make check (not run because uv dependency resolution hit private index/auth issues previously)
  • Manually verified behavior via targeted server tests, full server test suite, server lint, and server mypy using the existing .venv

Checklist

  • Linked issue/spec (Phase 2 from OOTB controls technical spec)
  • Updated docs/examples for user-facing changes: N/A, no API/docs contract changes
  • Included any required follow-up tasks: Phase 3 Luna controls remain follow-up work

@codecov

codecov Bot commented Jun 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 11 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...nt_control_server/bootstrap/out_of_box_controls.py 93.33% 7 Missing ⚠️
...ver/src/agent_control_server/endpoints/controls.py 80.00% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

@namrataghadi-galileo namrataghadi-galileo changed the base branch from main to feature/67101-out-of-box-controls June 30, 2026 17:12
r"\b(?:rm\s+-rf\s+(?:/|~|\$HOME)|sudo\s+rm\s+-rf|"
r"mkfs(?:\.[a-z0-9]+)?|dd\s+if=[^\s]+\s+of=/dev/[^\s]+|"
r"chmod\s+-R\s+777\s+/|chown\s+-R\s+[^|;&]*\s+/|"
r"shutdown\s+(?:-h\s+)?now|reboot)\b"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The shared trailing \b cannot match after / or ~, so rm -rf /, rm -rf ~, chmod -R 777 /, and chown -R root / pass this control. Please use explicit end/separator handling per alternative and add these cases to the test.

"anyOf": [
{
"required": ["approved"],
"properties": {"approved": {"const": True}},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could we avoid treating a caller-controlled approved flag as authorization evidence? A prompt-injected agent can set or replay it, while tools without this field cannot complete the flow. This should use a trusted approval artifact or host workflow bound to the action; the outbound template below has the same issue.

name="oob-sensitive-tool-requires-approved-role",
data=_leaf_control_payload(
description="Deny sensitive tool use when runtime context has an unapproved role.",
selector_path="context.user.role",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This fails open when context.user.role is missing: the selector returns None, and ListEvaluator returns matched=False before applying match_on="no_match". Please fail closed for missing/unknown roles and source the role from trusted principal context.

selector_path="name",
evaluator_name="list",
evaluator_config={
"values": ["search", "web_search", "retrieve", "calculator"],

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Exact bare names do not match integrations that qualify tool names. Google ADK emits names such as <agent>.web_search, so this denies an explicitly approved tool. Please normalize tool identity across integrations or match a canonical raw name, with an integration-level test.

try:
if namespace_key == default_out_of_box_namespace_key():
return
if await _namespace_has_active_controls(db, namespace_key=namespace_key):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Checking for any active control makes partial seeding permanent: templates commit separately, so one transient failure leaves rows that cause every later request to return here. It also skips tenants with one custom control. Please check expected seed identities or always run the idempotent per-template pass.

return
if await _namespace_has_active_controls(db, namespace_key=namespace_key):
return
await seed_out_of_box_controls(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This path is reached from a GET authorized only with controls.read, but it commits Control and ControlVersion rows. Could seeding move to trusted tenant provisioning, or require controls.create resolved to the same namespace, so read-only callers cannot trigger writes?

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.

2 participants