QueryContext lets you check what operations would be permitted by a capability set without actually applying the sandbox. This is useful for validation, testing, and building permission-checking UIs.
Constructor
QueryContext(caps: CapabilitySet)
Create a query context from a capability set.
The capability set to query against.
from nono_py import CapabilitySet, AccessMode, QueryContext
caps = CapabilitySet()
caps.allow_path("/tmp", AccessMode.READ_WRITE)
caps.block_network()
ctx = QueryContext(caps)
The context captures a snapshot of the capabilities at creation time. Changes to the original CapabilitySet after creating the context are not reflected.
Methods
query_path
query_path(path: str, mode: AccessMode) -> dict
Check if a path operation would be permitted.
Returns: Dictionary with query result.
Allowed Result
When the operation would be permitted:
{
"status": "allowed",
"reason": "granted_path",
"granted_path": "/private/tmp",
"access": "read+write"
}
| Field | Description |
|---|
status | Always "allowed" |
reason | Always "granted_path" |
granted_path | The capability that grants access |
access | The access level of that capability |
Denied Results
When the operation would be blocked:
Path not granted:
{
"status": "denied",
"reason": "path_not_granted"
}
Insufficient access level:
{
"status": "denied",
"reason": "insufficient_access",
"granted": "read",
"requested": "write"
}
| Field | Description |
|---|
status | Always "denied" |
reason | "path_not_granted" or "insufficient_access" |
granted | (Only for insufficient_access) The granted access level |
requested | (Only for insufficient_access) The requested access level |
Example
from nono_py import CapabilitySet, AccessMode, QueryContext
caps = CapabilitySet()
caps.allow_path("/tmp", AccessMode.READ)
ctx = QueryContext(caps)
# Allowed: path is covered with sufficient access
result = ctx.query_path("/tmp/file.txt", AccessMode.READ)
print(result)
# {'status': 'allowed', 'reason': 'granted_path',
# 'granted_path': '/private/tmp', 'access': 'read'}
# Denied: insufficient access
result = ctx.query_path("/tmp/file.txt", AccessMode.WRITE)
print(result)
# {'status': 'denied', 'reason': 'insufficient_access',
# 'granted': 'read', 'requested': 'write'}
# Denied: path not granted
result = ctx.query_path("/etc/passwd", AccessMode.READ)
print(result)
# {'status': 'denied', 'reason': 'path_not_granted'}
query_network
Check if network access would be permitted.
Returns: Dictionary with query result.
Allowed Result
{
"status": "allowed",
"reason": "network_allowed"
}
Denied Result
{
"status": "denied",
"reason": "network_blocked"
}
Example
from nono_py import CapabilitySet, QueryContext
# Network allowed by default
caps = CapabilitySet()
ctx = QueryContext(caps)
result = ctx.query_network()
print(result) # {'status': 'allowed', 'reason': 'network_allowed'}
# Block network
caps.block_network()
ctx = QueryContext(caps)
result = ctx.query_network()
print(result) # {'status': 'denied', 'reason': 'network_blocked'}
Use Cases
Configuration Validation
Validate a capability configuration before deployment:
def validate_config(caps: CapabilitySet, required_paths: list[tuple[str, AccessMode]]) -> bool:
ctx = QueryContext(caps)
for path, mode in required_paths:
result = ctx.query_path(path, mode)
if result["status"] == "denied":
print(f"Missing permission: {path} ({mode})")
return False
return True
# Check that all required paths are covered
caps = CapabilitySet()
caps.allow_path("/data", AccessMode.READ_WRITE)
required = [
("/data/input", AccessMode.READ),
("/data/output", AccessMode.WRITE),
]
if validate_config(caps, required):
apply(caps)
Permission Checking UI
Build an interactive permission checker:
def check_operation(ctx: QueryContext, path: str, mode: AccessMode) -> str:
result = ctx.query_path(path, mode)
if result["status"] == "allowed":
return f"ALLOWED via {result['granted_path']}"
elif result["reason"] == "insufficient_access":
return f"DENIED: have {result['granted']}, need {result['requested']}"
else:
return "DENIED: path not granted"
Testing Capability Sets
Write tests for your sandbox configuration:
import pytest
from nono_py import CapabilitySet, AccessMode, QueryContext
def test_sandbox_config():
caps = CapabilitySet()
caps.allow_path("/tmp", AccessMode.READ_WRITE)
caps.allow_path("/data", AccessMode.READ)
caps.block_network()
ctx = QueryContext(caps)
# Verify expected permissions
assert ctx.query_path("/tmp/file", AccessMode.WRITE)["status"] == "allowed"
assert ctx.query_path("/data/input", AccessMode.READ)["status"] == "allowed"
assert ctx.query_path("/data/input", AccessMode.WRITE)["status"] == "denied"
assert ctx.query_network()["status"] == "denied"