The nested permission bomb
Give a workflow its own scoped credential and an authored HITL policy, instead of letting it inherit the operator’s full access.
Reusing a human’s own credentials for an automated workflow is a common shortcut, but it turns a prompt injection into direct production access, makes your audit log attribute the workflow’s actions to a person, and creates unbounded delegation with no permission boundary. Excessive agency and over-privileged automation are named risks in both the NIST AI Risk Management Framework and the ACSC secure-AI guidance. The fix is to author the workflow’s identity and its boundary explicitly, rather than inherit them.
Prerequisites
contenoxinstalled.- An API, or a filesystem/shell workflow, you want an assistant to use with its own scoped access.
Steps
-
Register the tool with a service-level credential — not your personal token — and inject a caller identity hidden from the model:
contenox tools add warehouse-api \ --url https://api.internal.com \ --header "Authorization: Bearer $WORKFLOW_SCOPED_TOKEN" \ --inject "caller_id=workflow-finance-01" \ --inject "invoked_by=tom"--injectvalues are stripped from the tool schema before the model ever sees it; the engine merges them into every call at execution time. The model can’t read, tamper with, or omit them, and your logs now attribute the call to the workflow identity, not to whoever happened to be logged in. -
For a large API, register only a hand-curated subset rather than the whole surface — see Authoring your tool inventory for the
--specflag in full. A workflow whose job is reading warehouse stock shouldn’t also have adelete_shipmenttool just because the vendor’s OpenAPI spec includes it. -
Author a HITL policy that denies credential/secret paths outright, requires approval for writes, and allows harmless reads without interruption:
{ "default_action": "approve", "rules": [ {"tools": "local_fs", "tool": "*", "action": "deny", "when": [{"key": "path", "op": "glob", "value": "**/{.ssh,.gnupg,.aws,.azure,.kube,.config/gcloud}/**"}]}, {"tools": "local_fs", "tool": "*", "action": "deny", "when": [{"key": "path", "op": "glob", "value": "**/{.password-store,.local/share/keyrings,Library/Keychains,.config/1Password}/**"}]}, {"tools": "local_fs", "tool": "*", "action": "deny", "when": [{"key": "path", "op": "glob", "value": "**/{.bash_history,.zsh_history,.netrc,.npmrc}"}]}, {"tools": "local_shell", "tool": "local_shell", "action": "deny"}, {"tools": "webtools", "tool": "web_post", "action": "deny"}, {"tools": "local_fs", "tool": "write_file", "action": "approve"}, {"tools": "local_fs", "tool": "sed", "action": "approve"}, {"tools": "local_fs", "tool": "read_file", "action": "allow"}, {"tools": "local_fs", "tool": "list_dir", "action": "allow"} ] } -
Save it and activate it:
contenox config set hitl-policy-name hitl-policy-<name>.json
Expected outcome
- Logs show the workflow’s own identity (
caller_id,invoked_by) on every call, not a human’s. - Reads of
.ssh,.aws, shell history, and similar credential paths are denied outright. local_shellandweb_postare denied entirely under this policy;write_fileandsedpause for approval;read_fileandlist_dirpass silently.- Revoking the workflow’s access means rotating its own scoped token — no human’s credentials are touched.
Where to next
- Any API, a tool you authored — the same
--header/--inject/--specmechanism, worked end to end. - Authoring your tool inventory — curating a narrow OpenAPI subset instead of registering a whole API.
- The pause is yours to define — writing and switching HITL policies.
- HITL policies — the full policy schema.