feat: deliver owner-bound credentials into bwrap commands
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0726e-5232-73f2-aaca-2c05ceb62efb
This commit is contained in:
parent
0196f083c4
commit
17d4160b6e
10 changed files with 505 additions and 6 deletions
103
scripts/smoke-bwrap-credentials.py
Normal file
103
scripts/smoke-bwrap-credentials.py
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
"""Synthetic exec-env provider proof. Never fetches a live secret."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from sandboxer.extensions.bwrap import BwrapExtension
|
||||
from sandboxer.models import Profile
|
||||
|
||||
|
||||
def main():
|
||||
with tempfile.TemporaryDirectory(prefix="sandboxer-credential-proof-") as temporary:
|
||||
root = Path(temporary)
|
||||
provider = root / "synthetic-provider.py"
|
||||
provider.write_text("""import os,subprocess,sys
|
||||
assert 'ANTHROPIC_API_KEY' not in os.environ
|
||||
env=os.environ.copy()
|
||||
env['ANTHROPIC_API_KEY']='synthetic-only-credential-proof'
|
||||
raise SystemExit(subprocess.call(sys.argv[sys.argv.index('--')+1:],env=env))
|
||||
""")
|
||||
context = {
|
||||
"actor": "agt",
|
||||
"project": "credential-proof",
|
||||
"run_id": "proof-1",
|
||||
"profile_id": "profile.credential-proof",
|
||||
}
|
||||
extension = BwrapExtension(
|
||||
{
|
||||
"base_dir": str(root / "sandboxes"),
|
||||
"credential_routes": {
|
||||
"synthetic-proof": {
|
||||
"profiles": ["profile.credential-proof"],
|
||||
"projects": ["credential-proof"],
|
||||
"actors": ["agt"],
|
||||
"exec_argv": [sys.executable, str(provider), "--"],
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
profile = Profile(id=context["profile_id"], version="1", extension="ext.bwrap")
|
||||
handle = extension.provision(profile, {}, "localhost")
|
||||
try:
|
||||
extension.wait_ready(handle)
|
||||
result = extension.execute(
|
||||
handle,
|
||||
[
|
||||
"python3",
|
||||
"-c",
|
||||
"import os; "
|
||||
"assert os.environ['ANTHROPIC_API_KEY']=='synthetic-only-credential-proof'; "
|
||||
"assert 'BAO_TOKEN' not in os.environ and 'VAULT_TOKEN' not in os.environ; "
|
||||
"print(os.environ['ANTHROPIC_API_KEY'])",
|
||||
],
|
||||
credential_route_refs=["synthetic-proof"],
|
||||
execution_context=context,
|
||||
timeout_seconds=10,
|
||||
max_output_bytes=1024,
|
||||
)
|
||||
assert result["exit_code"] == 0 and result["stdout"] == "[REDACTED]\n"
|
||||
clean = extension.execute(
|
||||
handle,
|
||||
["python3", "-c", "import os; assert 'ANTHROPIC_API_KEY' not in os.environ"],
|
||||
credential_route_refs=[],
|
||||
execution_context=context,
|
||||
timeout_seconds=10,
|
||||
max_output_bytes=1024,
|
||||
)
|
||||
assert clean["exit_code"] == 0
|
||||
try:
|
||||
extension.execute(
|
||||
handle,
|
||||
["true"],
|
||||
credential_route_refs=["synthetic-proof"],
|
||||
execution_context={**context, "project": "wrong"},
|
||||
timeout_seconds=10,
|
||||
max_output_bytes=1024,
|
||||
)
|
||||
except ValueError:
|
||||
denied = True
|
||||
else:
|
||||
raise AssertionError("wrong consumer accepted")
|
||||
finally:
|
||||
teardown = extension.teardown(handle)
|
||||
assert teardown["workspace_removed"] == "True"
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"sandbox_id": handle["sandbox_id"],
|
||||
"synthetic_provider": True,
|
||||
"child_received_value": True,
|
||||
"output_redacted": True,
|
||||
"no_following_exec_leak": True,
|
||||
"wrong_project_denied": denied,
|
||||
"workspace_removed": True,
|
||||
"real_key_read": False,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue