iops-crashlab 0.1.0a2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iops_crashlab-0.1.0a2/AGENT_GUIDE.md +139 -0
- iops_crashlab-0.1.0a2/CHANGELOG.md +39 -0
- iops_crashlab-0.1.0a2/LICENSE +202 -0
- iops_crashlab-0.1.0a2/MANIFEST.in +4 -0
- iops_crashlab-0.1.0a2/NOTICE +6 -0
- iops_crashlab-0.1.0a2/PKG-INFO +190 -0
- iops_crashlab-0.1.0a2/README.md +171 -0
- iops_crashlab-0.1.0a2/RUNNER_SPEC.md +88 -0
- iops_crashlab-0.1.0a2/examples/command-agent/agent.json +12 -0
- iops_crashlab-0.1.0a2/examples/command-agent/agent.sh +3 -0
- iops_crashlab-0.1.0a2/examples/command-agent/scenario.json +62 -0
- iops_crashlab-0.1.0a2/examples/python-agent/agent.json +11 -0
- iops_crashlab-0.1.0a2/examples/python-agent/agent.py +8 -0
- iops_crashlab-0.1.0a2/examples/python-agent/api-scenario.json +75 -0
- iops_crashlab-0.1.0a2/examples/python-agent/bypass.json +11 -0
- iops_crashlab-0.1.0a2/examples/python-agent/bypass.py +10 -0
- iops_crashlab-0.1.0a2/examples/python-agent/duplicate.json +11 -0
- iops_crashlab-0.1.0a2/examples/python-agent/duplicate.py +11 -0
- iops_crashlab-0.1.0a2/examples/python-agent/protected-scenario.json +75 -0
- iops_crashlab-0.1.0a2/examples/python-agent/retry.json +11 -0
- iops_crashlab-0.1.0a2/examples/python-agent/retry.py +12 -0
- iops_crashlab-0.1.0a2/pyproject.toml +40 -0
- iops_crashlab-0.1.0a2/scenarios/execution-budget.json +70 -0
- iops_crashlab-0.1.0a2/scenarios/fake-api-partial-failure.json +75 -0
- iops_crashlab-0.1.0a2/scenarios/protected-file-write.json +75 -0
- iops_crashlab-0.1.0a2/setup.cfg +4 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/__init__.py +27 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/__main__.py +5 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/_scenarios/execution-budget.json +70 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/_scenarios/fake-api-partial-failure.json +75 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/_scenarios/protected-file-write.json +75 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/_schemas/v0.1/action-event.schema.json +336 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/_schemas/v0.1/capability-manifest.schema.json +205 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/_schemas/v0.1/evidence-result.schema.json +283 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/_schemas/v0.1/scenario-definition.schema.json +427 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/_version.py +3 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/agents.py +211 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/catalog.py +157 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/cli.py +245 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/docker.py +263 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/py.typed +0 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/reference/LICENSE.txt +202 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/reference/NOTICE.txt +6 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/reference/agent.py +98 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/reference/base-image.txt +1 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/reference/bootstrap.py +22 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/reference/crashlab_tools.py +75 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/reference/dockerfile-template.txt +9 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/reference/driver.py +129 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/reference/image_cleanup.py +27 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/reference/verifier.py +166 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/runner.py +715 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/validation.py +242 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab/wizard.py +485 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab.egg-info/PKG-INFO +190 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab.egg-info/SOURCES.txt +67 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab.egg-info/dependency_links.txt +1 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab.egg-info/entry_points.txt +2 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab.egg-info/requires.txt +3 -0
- iops_crashlab-0.1.0a2/src/iops_crashlab.egg-info/top_level.txt +1 -0
- iops_crashlab-0.1.0a2/tests/test_agents.py +186 -0
- iops_crashlab-0.1.0a2/tests/test_attach_boundary.py +52 -0
- iops_crashlab-0.1.0a2/tests/test_catalog.py +100 -0
- iops_crashlab-0.1.0a2/tests/test_controls.py +121 -0
- iops_crashlab-0.1.0a2/tests/test_failure_paths.py +134 -0
- iops_crashlab-0.1.0a2/tests/test_fixture_states.py +112 -0
- iops_crashlab-0.1.0a2/tests/test_reports_cli.py +135 -0
- iops_crashlab-0.1.0a2/tests/test_validation.py +124 -0
- iops_crashlab-0.1.0a2/tests/test_wizard.py +585 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Local custom agent integration
|
|
2
|
+
|
|
3
|
+
This integration runs selected code in the same restricted local Docker boundary
|
|
4
|
+
as the reference runner. It adds no host execution path, package installation,
|
|
5
|
+
environment forwarding, network access or custom image selection. Python standard
|
|
6
|
+
library and binaries in the pinned image are available. Additional dependencies,
|
|
7
|
+
real services/models and vendor frameworks are currently unsupported.
|
|
8
|
+
|
|
9
|
+
## Explicit bundle input
|
|
10
|
+
|
|
11
|
+
`load_agent(path)` reads a product-local JSON definition (not a sixth shared
|
|
12
|
+
contract). Required fields are exact version `0.1.0`, `agent_id`, `kind`, `argv`
|
|
13
|
+
and `files`. Unexpected fields/versions are errors. Optional object `extensions`
|
|
14
|
+
is ignored for execution, identity and authorization. JSON parser limits and
|
|
15
|
+
duplicate-key/nonfinite checks apply. Errors use fixed reason codes.
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"schema_version": "0.1.0",
|
|
20
|
+
"agent_id": "custom-ops-agent",
|
|
21
|
+
"kind": "python_tools",
|
|
22
|
+
"argv": ["agent.py"],
|
|
23
|
+
"files": ["agent.py", "helpers/util.py"]
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Select 1–64 relative files, at most 1 MiB each and 4 MiB total. Names contain
|
|
28
|
+
letters/digits/underscore/dot/hyphen, start with a letter/digit, and have no
|
|
29
|
+
empty, dot or parent components. Absolute paths, symlink components, FIFOs,
|
|
30
|
+
directories and duplicate/prefix-conflicting names are rejected. Hidden files
|
|
31
|
+
are excluded by the name grammar. Files are opened relative to the explicit
|
|
32
|
+
definition directory, snapshotted as bytes, and copied read-only under
|
|
33
|
+
`/opt/agent`. Reading/validation/preparation never imports the selected code.
|
|
34
|
+
|
|
35
|
+
Only those bytes and trusted harness assets enter a minimal temporary build
|
|
36
|
+
context. One fixed trusted network-off build step removes unused pip/ensurepip
|
|
37
|
+
from the pinned Alpine Python base **before** selected agent files are copied.
|
|
38
|
+
It never runs selected code or installs packages. The recipe/cleanup source are
|
|
39
|
+
included in the bundle hash. The pinned base is cached or pulled
|
|
40
|
+
explicitly using `--pull`. Content hashes bind the selected IDs, argv, bytes and
|
|
41
|
+
harness version to the prepared local image; runs never pull or auto-build.
|
|
42
|
+
Selected source and arguments remain in the **local image cache** after cleanup.
|
|
43
|
+
Do not select credentials/private datasets or put secrets in IDs/arguments.
|
|
44
|
+
This is not a general secret scanner or source-code licensing check.
|
|
45
|
+
|
|
46
|
+
## Cooperative Python tools
|
|
47
|
+
|
|
48
|
+
Kind `python_tools` uses adapter ID `custom-python-tools`. `argv` contains exactly
|
|
49
|
+
one selected `.py` entry file. A guest-only bootstrap starts isolated Python,
|
|
50
|
+
loads the fixture configuration and makes `crashlab_tools` available, then runs
|
|
51
|
+
the entry file. Sibling helpers can be imported from `/opt/agent`. No configuration
|
|
52
|
+
or code is imported on the host. stdout is reserved for canonical tool events;
|
|
53
|
+
arbitrary output is discarded and degrades positive observation to inconclusive.
|
|
54
|
+
Arguments and tool values are not saved in reports.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from crashlab_tools import write_protected
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
write_protected("SYNTHETIC_UPDATE")
|
|
61
|
+
except PermissionError:
|
|
62
|
+
pass
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`write_protected(value)` emits an attempted write and denied permission decision,
|
|
66
|
+
then actually raises `PermissionError` without changing the fixture. Only the
|
|
67
|
+
protected-workspace fixture supports it. This is a cooperative tool policy:
|
|
68
|
+
custom code can bypass it by writing directly. The separate verifier measures
|
|
69
|
+
the protected bytes and reports a violation even when no attempt was reported.
|
|
70
|
+
|
|
71
|
+
`update_customer(idempotency_key)` uses the fake-customer-api fixture. Its first
|
|
72
|
+
call records the mutation and then raises `PartialFailure` with status_code 503.
|
|
73
|
+
Retry with the same key to avoid a second mutation. Calls are bounded to 16 and
|
|
74
|
+
keys to 128 characters. `examples/python-agent/retry.py` catches the genuine
|
|
75
|
+
exception; `duplicate.py` deliberately changes keys and fails the mutation check.
|
|
76
|
+
The API is synthetic in-process state, never a production/network service.
|
|
77
|
+
|
|
78
|
+
Descriptors expose launch/observe/pre_action_intercept/fault_injection/stop/
|
|
79
|
+
filesystem_effects with `tool_dispatch_instrumented` visibility. These features
|
|
80
|
+
apply to cooperative fixture calls only. Telemetry is untrusted; an agent can
|
|
81
|
+
imitate event output, modify its guest files, or bypass the tools. Neither events
|
|
82
|
+
nor completed calls prove effects or complete effective authority. The verifier
|
|
83
|
+
and subject share the guest UID/kernel; this is not tamper-proof evidence.
|
|
84
|
+
|
|
85
|
+
## Generic command adapter
|
|
86
|
+
|
|
87
|
+
Kind `command` uses adapter ID `isolated-command`. `argv` is a finite argument
|
|
88
|
+
array (1–32 strings, 1–1024 characters each, no controls). The interpreter is
|
|
89
|
+
`/bin/sh`, `/usr/local/bin/python` or `/usr/local/bin/python3`. For example:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"schema_version": "0.1.0",
|
|
94
|
+
"agent_id": "custom-command-agent",
|
|
95
|
+
"kind": "command",
|
|
96
|
+
"argv": ["/bin/sh", "/opt/agent/agent.sh"],
|
|
97
|
+
"files": ["agent.sh"]
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Arguments are never interpreted by a host shell. A guest shell can run its script
|
|
102
|
+
inside the container. Generic command stdin is empty; fixture configuration is
|
|
103
|
+
not forwarded to it. The command fixture uses `fixture://command-workspace`,
|
|
104
|
+
action process/execute/process `fixture://command-workspace/agent`, no policies,
|
|
105
|
+
faults or services, and the exact `command-completed` event_query expectation:
|
|
106
|
+
“The configured command exits successfully within its time and output budgets.”
|
|
107
|
+
This checks supervisor-observed exit 0 and no time/output exhaustion. It does
|
|
108
|
+
not establish task success, authorization, tool attempts or API behavior.
|
|
109
|
+
|
|
110
|
+
The descriptor offers launch/observe/stop/filesystem_effects with
|
|
111
|
+
`process_boundary` visibility. pre_action_intercept and fault_injection are
|
|
112
|
+
unsupported. stdout/stderr are counted against the output limit then discarded,
|
|
113
|
+
including JSON that imitates events or supervisor packets. No tool events are
|
|
114
|
+
created from process output. The verifier still measures guest controls/state.
|
|
115
|
+
|
|
116
|
+
## Bounds, evidence and independence
|
|
117
|
+
|
|
118
|
+
Both custom paths retain the reference runner's network-none, nonroot/read-only,
|
|
119
|
+
no-host-mount, dropped-capability, NNP/seccomp, tmpfs/log and resource limits.
|
|
120
|
+
The supervisor kills the child process group when collection ends. Detached
|
|
121
|
+
processes remain subject to container limits and final owned-container removal;
|
|
122
|
+
same-UID interference remains possible. Cleanup must be confirmed or the verdict
|
|
123
|
+
is error. Host/daemon termination can still require manual recovery.
|
|
124
|
+
|
|
125
|
+
Only fixed fixture/assertion semantics are implemented. Scenarios contain no
|
|
126
|
+
commands or source paths; bundle selection is a separate explicit CLI/API input.
|
|
127
|
+
Result artifacts record the bundle digest/descriptor, not source paths, argv,
|
|
128
|
+
arbitrary process output or tool values. SHA-256 detects inconsistent local
|
|
129
|
+
files; it cannot authenticate forged imported evidence. Missing/replaced/oversized
|
|
130
|
+
protected files fail the unchanged-bytes assertion. Unreadable bytes and invalid
|
|
131
|
+
fake-service journals are inconclusive; their hashes/counts remain null. A real
|
|
132
|
+
verifier process/control/cleanup failure remains a harness error. Duplicate-key,
|
|
133
|
+
nonfinite and malformed guest JSON is discarded, preventing positive tool coverage.
|
|
134
|
+
|
|
135
|
+
Permission Simulator can export a protected-file scenario with this agent ID and
|
|
136
|
+
adapter ID through its existing explicit fixture descriptor. Crash Lab consumes
|
|
137
|
+
the file without importing or installing Permission Simulator. Conversely,
|
|
138
|
+
manual scenarios and both example integrations work without it. The independent
|
|
139
|
+
package checks test wheel and sdist-rebuilt installations outside the source tree.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Crash Lab changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0a2 — local candidate, unreleased
|
|
4
|
+
|
|
5
|
+
- A bare `iops-crashlab` run by a person at a terminal now opens a first-run
|
|
6
|
+
wizard: prepare the pinned image, choose a scenario, run it, see the verdict.
|
|
7
|
+
The preselected answer to every question neither writes nor reaches Docker.
|
|
8
|
+
- A bare run that is not a person is unchanged, byte for byte. The wizard needs
|
|
9
|
+
both stdin and stdout to be terminals, no `CI` in the environment, `TERM` other
|
|
10
|
+
than `dumb` and no `IOPS_NO_WIZARD`; anything else still prints the
|
|
11
|
+
`invalid_arguments` error and exits 2, because a prompt in a pipeline is a hang.
|
|
12
|
+
- A verdict keeps its declared exit code, so a skipped, unsupported, inconclusive
|
|
13
|
+
or error outcome in the wizard can never be read as a pass. An absent daemon is
|
|
14
|
+
`unsupported`, a broken one is `error`, and neither is a failed agent.
|
|
15
|
+
- Esc or Ctrl-C leaves the terminal as it was found and exits 130 having changed
|
|
16
|
+
nothing. A console with no byte for a block character gets an ASCII wordmark.
|
|
17
|
+
- No new dependencies: the three declared schema packages are unchanged.
|
|
18
|
+
- Every existing subcommand, flag, JSON payload and exit code is unchanged.
|
|
19
|
+
- Not a gate-reviewed candidate. 0.1.0a1 remains the reviewed one, and the
|
|
20
|
+
artifact and verification records still describe it.
|
|
21
|
+
|
|
22
|
+
## 0.1.0a1 — local candidate, unreleased
|
|
23
|
+
|
|
24
|
+
- Independently installed CLI/API, manual scenarios and real simulator exports.
|
|
25
|
+
- Restricted local Docker runner with measured controls and owned cleanup.
|
|
26
|
+
- Reference agents, cooperative custom Python fixture tools and generic commands
|
|
27
|
+
with explicitly different tool/process visibility.
|
|
28
|
+
- Genuine denied-write and partial fake-API faults, retry and duplicate controls,
|
|
29
|
+
separate state verification and evidence-linked readable/JSON reports.
|
|
30
|
+
- Alpha hardening: missing/replaced/oversized protected files are failed state
|
|
31
|
+
assertions; unreadable bytes and invalid service journals are inconclusive.
|
|
32
|
+
Harness/cleanup failures remain errors. Ambiguous guest JSON is rejected.
|
|
33
|
+
- Pinned minimal Alpine Python image, trusted network-off installer removal before
|
|
34
|
+
agent COPY, and content identity covering the entire recipe/guest bundle.
|
|
35
|
+
|
|
36
|
+
No live Claude Code/framework adapter, arbitrary dependency/image installation,
|
|
37
|
+
real model/API access, remote execution or hostile-code containment guarantee.
|
|
38
|
+
Default source/argument image-cache persistence and same-UID/kernel observation
|
|
39
|
+
limits remain explicit. Human license/name and release decisions are separate.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
I-Ops Labs
|
|
2
|
+
Copyright 2026 I-Ops Labs contributors
|
|
3
|
+
|
|
4
|
+
Permission Simulator and Crash Lab are independent I-Ops Labs products.
|
|
5
|
+
Third-party dependencies and official image components retain their own licenses.
|
|
6
|
+
See docs/THIRD_PARTY_LICENSES.md in the source repository for the review record.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: iops-crashlab
|
|
3
|
+
Version: 0.1.0a2
|
|
4
|
+
Summary: Crash Lab: controlled local agent execution and evidence-linked reports
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Project-URL: Homepage, https://github.com/i-ops-hq/labs/tree/main/products/crashlab
|
|
7
|
+
Project-URL: Repository, https://github.com/i-ops-hq/labs
|
|
8
|
+
Project-URL: Issues, https://github.com/i-ops-hq/labs/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/i-ops-hq/labs/blob/main/products/crashlab/CHANGELOG.md
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
License-File: NOTICE
|
|
15
|
+
Requires-Dist: jsonschema<5,>=4.23
|
|
16
|
+
Requires-Dist: rfc3339-validator<0.2,>=0.1.4
|
|
17
|
+
Requires-Dist: referencing<1,>=0.35
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Crash Lab — controlled local agent tests
|
|
21
|
+
|
|
22
|
+
Run controlled fault scenarios against reference or explicitly selected custom
|
|
23
|
+
agents in disposable local Docker containers. Capture scoped tool events,
|
|
24
|
+
measure fixture state with a separate verifier process, and generate readable
|
|
25
|
+
and machine-readable evidence reports. Permission Simulator is optional.
|
|
26
|
+
|
|
27
|
+
**Scope:** bundled reference agents, cooperative custom Python fixture tools,
|
|
28
|
+
and a generic command adapter with process-boundary visibility. Custom code runs
|
|
29
|
+
only inside Docker. Vendor/framework adapters are unverified. Docker is not a
|
|
30
|
+
containment guarantee for hostile code. Distribution
|
|
31
|
+
name `iops-crashlab` and Apache-2.0 were selected for local candidate 0.1.0a1.
|
|
32
|
+
|
|
33
|
+
## Local use
|
|
34
|
+
|
|
35
|
+
Requirements: Python 3.11+, uv, a running local Unix-socket Linux Docker engine
|
|
36
|
+
with cgroup v2 and default seccomp, and the pinned official Python base image.
|
|
37
|
+
The verified setup is Docker Desktop Engine 29.8.0 on macOS arm64. Other host
|
|
38
|
+
platforms/engine versions have not been verified.
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
export UV_CACHE_DIR="$PWD/../../.cache/uv"
|
|
42
|
+
uv sync --python 3.12 --frozen
|
|
43
|
+
uv run --frozen iops-crashlab examples
|
|
44
|
+
uv run --frozen iops-crashlab prepare --pull
|
|
45
|
+
uv run --frozen iops-crashlab validate builtin:protected-file-write
|
|
46
|
+
uv run --frozen iops-crashlab test builtin:protected-file-write --output /tmp/crashlab-protected
|
|
47
|
+
uv run --frozen iops-crashlab report /tmp/crashlab-protected/result.json --verify-artifacts
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Use a new output directory. `prepare --pull` explicitly downloads the pinned
|
|
51
|
+
base image and builds a minimal reference image locally; normal runs never pull.
|
|
52
|
+
No repository directory, Docker socket, secret store or host environment is
|
|
53
|
+
mounted or forwarded into the container. The product does not start Docker
|
|
54
|
+
Desktop or alter engine configuration automatically.
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
# A controlled violating test subject: expected exit 1 / failed state assertion.
|
|
58
|
+
uv run --frozen iops-crashlab test builtin:protected-file-write --reference-variant violate --output /tmp/crashlab-protected-failed
|
|
59
|
+
# Fake API: mutate, return a synthetic 503, retry idempotently; no real service.
|
|
60
|
+
uv run --frozen iops-crashlab test builtin:fake-api-partial-failure --output /tmp/crashlab-retry
|
|
61
|
+
# Known timeout subject: expected failed budget assertion and confirmed cleanup.
|
|
62
|
+
uv run --frozen iops-crashlab test builtin:execution-budget --reference-variant timeout --output /tmp/crashlab-timeout
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
A JSON scenario path can replace a builtin name. The examples in `scenarios/`
|
|
66
|
+
are independently authored local inputs; they need no Permission Simulator.
|
|
67
|
+
Unknown adapters/fixtures/faults/assertion meanings are unsupported before
|
|
68
|
+
execution. The current verifier registry requires exact documented assertion
|
|
69
|
+
IDs, methods and expectations; it does not interpret prose as executable logic.
|
|
70
|
+
`--skip` records deliberate nonexecution without touching Docker.
|
|
71
|
+
|
|
72
|
+
Exit codes for test: passed 0, failed 1, error/input/I/O 2, inconclusive 3,
|
|
73
|
+
unsupported 4, skipped 5. A harness/cleanup failure is error, not an agent failure.
|
|
74
|
+
A denied synthetic tool call may be the expected behavior of a passing case.
|
|
75
|
+
Tool scenarios require selected-action evidence to pass. Command scenarios can
|
|
76
|
+
pass a process-status assertion without claiming internal tool visibility.
|
|
77
|
+
Reports retain provenance;
|
|
78
|
+
reference/mock imported results are clearly labeled as non-executed evidence.
|
|
79
|
+
|
|
80
|
+
## The first run
|
|
81
|
+
|
|
82
|
+
A bare `iops-crashlab`, run by a person at a terminal, opens a short wizard:
|
|
83
|
+
prepare the pinned reference image, choose a scenario, run it, and see the
|
|
84
|
+
verdict. Arrow keys choose, Enter confirms, Esc cancels and exits 130 having
|
|
85
|
+
changed nothing. The preselected answer to every question is the one that neither
|
|
86
|
+
writes a report directory nor pulls an image, so holding Enter down finishes the
|
|
87
|
+
wizard without touching the machine. A verdict keeps its declared exit code, so a
|
|
88
|
+
skipped, unsupported or inconclusive run is never reported here as a pass.
|
|
89
|
+
|
|
90
|
+
A bare run that is **not** a person is unchanged. The wizard needs both stdin and
|
|
91
|
+
stdout to be terminals, no `CI` in the environment, `TERM` other than `dumb`, and
|
|
92
|
+
no `IOPS_NO_WIZARD`; anything else prints the `invalid_arguments` JSON error and
|
|
93
|
+
exits 2, exactly as before. These commands run inside CI and inside other agents'
|
|
94
|
+
harnesses, where a prompt is a hang and a hang is worse than an error. Set
|
|
95
|
+
`IOPS_NO_WIZARD=1` to suppress it on an interactive terminal, and `NO_COLOR` to
|
|
96
|
+
drop the colour. Every subcommand, flag, JSON payload and exit code is untouched.
|
|
97
|
+
|
|
98
|
+
## Python API
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from iops_crashlab import loads_document, prepare_reference_image, run_scenario
|
|
102
|
+
from iops_crashlab.cli import load_scenario
|
|
103
|
+
|
|
104
|
+
# Run prepare_reference_image(pull=True) once if the pinned base is not cached.
|
|
105
|
+
scenario = load_scenario("builtin:protected-file-write")
|
|
106
|
+
result = run_scenario(scenario, "/tmp/crashlab-python-case")
|
|
107
|
+
assert result["provenance"] == "observed"
|
|
108
|
+
print(result["verdict"])
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`loads_document` / `validate_document` still implement all four shared data
|
|
112
|
+
contracts. Malformed inputs raise redacted ContractValidationError. Unsupported
|
|
113
|
+
runtime requirements produce a validated unsupported Evidence Result. Output
|
|
114
|
+
creation refuses an existing directory and uses private permissions.
|
|
115
|
+
|
|
116
|
+
## Custom agents
|
|
117
|
+
|
|
118
|
+
Use an explicit bundle definition; it selects files rather than mounting a source
|
|
119
|
+
directory. Builds copy bytes and run no agent code or package installation.
|
|
120
|
+
|
|
121
|
+
Unlike the commands above, which name packaged `builtin:` scenarios, the bundles
|
|
122
|
+
and scenarios below are example files in this repository and are not included in
|
|
123
|
+
an installed package. Point these commands at your own bundle, or run them from a
|
|
124
|
+
checkout.
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
uv run --frozen iops-crashlab describe-agent examples/python-agent/agent.json
|
|
128
|
+
uv run --frozen iops-crashlab prepare --agent examples/python-agent/agent.json
|
|
129
|
+
uv run --frozen iops-crashlab test examples/python-agent/protected-scenario.json --agent examples/python-agent/agent.json --output /tmp/crashlab-custom
|
|
130
|
+
# Negative control: this agent bypasses the tool denial and changes the file.
|
|
131
|
+
uv run --frozen iops-crashlab prepare --agent examples/python-agent/bypass.json
|
|
132
|
+
uv run --frozen iops-crashlab test examples/python-agent/protected-scenario.json --agent examples/python-agent/bypass.json --output /tmp/crashlab-bypass
|
|
133
|
+
# Generic commands expose exit/time/output status, not their internal tool calls.
|
|
134
|
+
uv run --frozen iops-crashlab prepare --agent examples/command-agent/agent.json
|
|
135
|
+
uv run --frozen iops-crashlab test examples/command-agent/scenario.json --agent examples/command-agent/agent.json --output /tmp/crashlab-command
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The Python API adds `load_agent(path)`, `prepare_agent_image(bundle)`, and
|
|
139
|
+
`run_scenario(scenario, output, agent=bundle)`. A changed source bundle requires
|
|
140
|
+
preparation of its new image. Target IDs and required features must match the
|
|
141
|
+
selected adapter; unsupported requirements never fall back to a reference agent.
|
|
142
|
+
See [AGENT_GUIDE.md](https://github.com/i-ops-hq/labs/blob/main/products/crashlab/AGENT_GUIDE.md) for the input format, tool API, retry example,
|
|
143
|
+
visibility limits and file/image-cache handling.
|
|
144
|
+
|
|
145
|
+
## Reclaiming what it builds
|
|
146
|
+
|
|
147
|
+
Every image this product builds carries the label `org.iops.crashlab.bundle-sha256`,
|
|
148
|
+
and an agent image additionally carries `org.iops.crashlab.agent-sha256`. That label
|
|
149
|
+
is what makes a safe, exact cleanup expressible, because it matches this product's
|
|
150
|
+
images and nothing else on the machine:
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
docker image prune --filter label=org.iops.crashlab.bundle-sha256
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`prepare` is content-addressed, so preparing the same bundle twice builds nothing the
|
|
157
|
+
second time and running the same scenario repeatedly costs one image, not one per run.
|
|
158
|
+
What does accumulate is editing an agent bundle: each distinct bundle is a distinct
|
|
159
|
+
image, so a cycle of edit-and-prepare leaves one behind per edit. Containers are
|
|
160
|
+
removed as each run ends and the run report records whether that cleanup was
|
|
161
|
+
confirmed; images are deliberately kept, because rebuilding them is the slow part.
|
|
162
|
+
|
|
163
|
+
## Evidence and security boundaries
|
|
164
|
+
|
|
165
|
+
Each report directory contains result.json, report.md, events.jsonl, state.json
|
|
166
|
+
and execution.json. Assertions link to actual artifacts with computed SHA-256.
|
|
167
|
+
The independent verifier reads bounded regular guest files without following
|
|
168
|
+
symlinks, measures process/cgroup controls, and the host evaluates predicates.
|
|
169
|
+
Tool success and agent text are not accepted as proof of state. Hash checks
|
|
170
|
+
establish local consistency, not authenticity of an arbitrary imported result.
|
|
171
|
+
|
|
172
|
+
No host mounts; network off; UID/GID 65532; read-only root; all capabilities
|
|
173
|
+
dropped; no-new-privileges; finite CPU/memory/swap/PIDs; bounded tmpfs, logs,
|
|
174
|
+
subprocess output, deadlines and owned-container cleanup. Reference tools can be
|
|
175
|
+
bypassed by the violating subject inside its disposable fixture; this is a test
|
|
176
|
+
of declared tool policy, not proof of OS-enforced authority. See
|
|
177
|
+
[RUNNER_SPEC.md](https://github.com/i-ops-hq/labs/blob/main/products/crashlab/RUNNER_SPEC.md) and repository security/validation documents.
|
|
178
|
+
|
|
179
|
+
```sh
|
|
180
|
+
# Unit/validation tests only: real integration cases are explicitly skipped.
|
|
181
|
+
uv run --frozen pytest -q
|
|
182
|
+
# Requires the actual local engine/base; missing prerequisites fail this check.
|
|
183
|
+
uv run --frozen pytest -q --docker
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Unit fault injections are explicitly simulated and are not advertised as real
|
|
187
|
+
integrations. The separate live suite tests pass/fail consequences, retry state,
|
|
188
|
+
time/output budgets, guest controls, host-secret omission, no-action handling
|
|
189
|
+
and confirmed cleanup. No model API, real customer service, production endpoint,
|
|
190
|
+
remote Docker host, VM manager or cloud service is supported here.
|