Course: 2A — Building AI Harnesses for Cybersecurity Module: S00 — Legal, Ethics, and Rules of Engagement Duration: 60–90 minutes (three labs, one per sub-section) Environment: Local. No live targets. A text editor, a JSON validator, and access to public bug bounty policy pages (read-only).
These labs are deliberately non-technical in the scanning sense. They produce the legal artifacts — a scope file, a disclosure template, a retention policy, an authorization checklist — that the rest of the course's engineering builds on. The scope file you produce in Lab 1 is the literal input to S01's scope enforcement middleware. You are converting a contract into a control.
No special tooling. You need:
jq, Python's json.tool, an online linter).# Verify jq is available (used to validate your scope file)
jq --version # need 1.6+
That is the entire environment. The point of this module is that the legal layer is upstream of the engineering; you do not need a scanner to produce a defensible scope file.
Maps to: S00.1 — Authorization and Scope Law
Time: 20–25 minutes
Output: scope.json — the input to S01's scope enforcement middleware
Take a real bug bounty program policy (a contract written in English) and formalize it into a structured scope file that a harness can load and check on every outbound tool call. The discipline of converting prose scope into machine-checkable scope surfaces every ambiguity the prose hides.
Read both in full. You are looking for five things, and the prose will not always make them easy to find:
Record which of the five each policy makes explicit, and which it leaves ambiguous. The ambiguous ones are where the legal risk concentrates.
Produce scope.json using the schema below. Fill it from ONE of the two policies you read. Be literal — if the policy is ambiguous, mark the field null and note it; do not invent a value.
{
"engagement_id": "h1-<program-name>-2026-07",
"schema_version": "1.0.0",
"generated_at": "2026-07-08T12:00:00Z",
"valid_until": "2026-07-15T00:00:00Z",
"asset_owner": {
"name": "<the company that owns the assets>",
"authorization_basis": "<'direct program' | 'contractor-warranted' | unknown>"
},
"program_sponsor": {
"name": "<the program operator, if different from owner>",
"warrants_owner_authorization": true
},
"in_scope": [
{ "type": "domain", "value": "app.example.com", "match": "exact" },
{ "type": "domain", "value": "api.example.com", "match": "exact" }
],
"out_of_scope": [
{ "type": "domain", "value": "status.example.com", "reason": "third-party status page" },
{ "type": "domain_pattern", "value": "*.thirdparty-cdn.net", "reason": "third-party infrastructure" }
],
"rules_of_engagement": {
"max_concurrent_connections": 10,
"max_requests_per_second": 20,
"forbidden_actions": ["dos", "social_engineering", "data_exfiltration", "physical_testing"],
"minimum_proof_only": true,
"stop_after_validation": true
},
"safe_harbor": {
"granted": true,
"conditions": ["in_scope_only", "roe_compliant_only"],
"covers_third_parties": false
},
"disclosure": {
"policy": "CVD",
"coordination_window_days": 45,
"requires_vendor_signoff_for_publication": true
}
}
Validate the JSON: jq . scope.json > /dev/null (silent success = valid).
Stress-test your scope file by answering these questions against it. Each is a real authorization-chain question:
cdn.example.com. Is it in scope? (Does your in_scope cover it? Is it in out_of_scope?)max_requests_per_second, which should be ≤ the target's tolerance.)forbidden_actions — data_exfiltration should block this.)valid_until.)covers_third_parties — it should be false.)scope.json valid against the schema, filled from a real policyin_scope is an exhaustive list with explicit match semantics (exact, not implied wildcard), per the Disclose.io/HackerOne/Bugcrowd standard.out_of_scope is checked after in_scope matching — a domain in both lists is out of scope.rules_of_engagement.forbidden_actions includes at minimum dos, data_exfiltration, social_engineering.max_requests_per_second is a real number sourced from the policy (or a conservative default if unspecified — never unset).valid_until is a real timestamp, not null. Scope is re-verified each session.safe_harbor.covers_third_parties is false — this is the honest reading of every program's safe harbor.Maps to: S00.2 — Responsible Disclosure and Evidence Obligations
Time: 20 minutes
Output: disclosure-template.md and retention-policy.md
Write disclosure-template.md — a reusable timeline template a harness can stamp onto every finding. It must contain the minimum fields that make disclosure auditable:
# Coordinated Vulnerability Disclosure — Finding [ID]
## Timeline
| Date (UTC) | Event | Actor | Notes |
| --- | --- | --- | --- |
| YYYY-MM-DD | Discovery | Tester | Scope ref: <scope_ref> |
| YYYY-MM-DD | Private report submitted | Tester | Channel: <H1/Bugcrowd/email> |
| YYYY-MM-DD | Vendor acknowledgment expected | Vendor | Window: 7 days from report |
| YYYY-MM-DD | Fix expected (coordination window) | Vendor | ~45 days CERT/CC default (or program window) |
| YYYY-MM-DD | Coordinated public disclosure | Both | Requires vendor signoff: YES |
## Disclosure conditions
- [ ] Vendor has acknowledged
- [ ] Vendor has fixed OR coordination window has elapsed with vendor silence
- [ ] Vendor has signed off on public disclosure (if required)
- [ ] Proof redacted of any sensitive data before publication
## If vendor is unresponsive
Do NOT publish before the coordination window elapses. Early disclosure can void safe harbor.
Fill one instance of this template for a hypothetical finding (make up the dates and details). The point is to feel which fields the harness must auto-populate and which require human decision.
Write retention-policy.md. Define the four retention classes, their retention period, and their destruction step. This is the policy the harness's evidence logger enforces.
# Evidence Retention Policy
## Classification (applied at capture)
| Class | Definition | Retention | Destruction |
| --- | --- | --- | --- |
| Public | No sensitive data; non-exploitable artifact | Indefinite (portfolio) | N/A |
| Redacted | Sensitive data redacted at capture time | Engagement window + 90-day tail | Automated purge after tail |
| Restricted | Contains real PII, credentials, or proprietary data | Until report submission (or per contract) | Destroy on report; record the deletion |
| Destroyed | (Post-destruction record) | Indefinitely (the record of deletion, not the data) | N/A |
## Capture rules
- Default to NOT capturing. If unsure, classify Restricted and destroy early.
- Minimum proof only: COUNT(*) or 1 redacted row, never SELECT *.
- Credentials/secrets: report the exposure, redact in report, securely delete the captured key.
## Enforcement
The evidence logger classifies each record at capture and enforces retention automatically. A record classified Restricted is destroyed on report submission; the destruction is logged (we keep the record of the deletion, not the deleted data).
disclosure-template.md filled for one hypothetical finding, with all condition checkboxes presentretention-policy.md with all four classes defined, retention periods, and destruction stepsMaps to: S00.3 — Harness-Specific Legal Risks
Time: 15–20 minutes
Output: pre-engagement-checklist.md — runs before every harness engagement
Produce a pre-engagement authorization checklist that a human (or a gate node in the harness) runs before every engagement. Every item maps to a specific risk from S00.3. If any item fails, the engagement does not start.
# Pre-Engagement Authorization Checklist
## Engagement: [name] · Date: [YYYY-MM-DD]
### Authorization chain (S00.1)
- [ ] Asset owner identified and named
- [ ] Program sponsor warrants owner authorization (or = owner)
- [ ] Scope file present, validated (jq), and valid_until > now
- [ ] Scope file re-verified this session (not stale)
### Rules of engagement (S00.1)
- [ ] RoE rate caps present in scope file
- [ ] Forbidden actions list includes: dos, data_exfiltration, social_engineering
- [ ] minimum_proof_only = true
- [ ] Autonomy level set to ≤ 3 for production engagement
### Scope enforcement (S00.3 Risk 2)
- [ ] Scope enforcement middleware active
- [ ] Every outbound tool call passes through it (verified by a dry-run block)
- [ ] Scope_ref stamping confirmed on a test call
### Evidence handling (S00.2 + S00.3 Risk 3)
- [ ] Evidence logger active, append-only, scope-referenced
- [ ] Retention policy loaded (Public/Redacted/Restricted/Destroyed)
- [ ] Restricted-class destruction-on-report configured
### DoS / scanning controls (S00.3 Risk 1)
- [ ] Rate-limit middleware active on all scanning tools
- [ ] Distress-detection (5xx/reset/latency) configured as a stop condition
- [ ] Default concurrency set low
### Agentic escalation controls (S00.3 Risk 4)
- [ ] Kill switch tested (harness stops within 1 second)
- [ ] Blast radius bounded: single session cannot exceed one engagement's scope
- [ ] Credential scope capped (no host credential store access)
- [ ] Audit log captures scope_ref per call
### Disclosure readiness (S00.2)
- [ ] CVD timeline template loaded
- [ ] Program disclosure window known (45-day default or program-specified)
## Gate
If ANY unchecked: DO NOT START THE ENGAGEMENT. Fix the gap first.
Append a short disclaimer that ships with every engagement's output. This is not a substitute for legal review, but it establishes the engagement's basis explicitly:
## Authorization basis
This engagement is conducted under the authorization of [asset owner], as specified
in the scope file [scope.json, engagement_id]. Testing is limited to in-scope assets
and rules of engagement defined therein. All findings are produced under coordinated
vulnerability disclosure. This document is an engineering record, not legal advice;
for the specific engagement, authorization was reviewed on [date].
pre-engagement-checklist.md complete, with every risk from S00.3 mapped to at least one itemWrite a scope-file linter. A small script (jq or Python) that takes scope.json and fails if: any required field is null, valid_until is in the past, forbidden_actions is missing any of the four required entries, or in_scope is empty. This is the CI gate for scope files — scope files that fail the linter do not load into a harness.
Model the authorization chain as a state machine. In Mermaid or in code, model the chain from asset owner to tool call as a state machine where each transition checks a condition. Identify the three breaks (Sponsor≠Owner, action outside RoE, stale scope) as the transitions that can fail. This is a direct warm-up for S01's scope enforcement implementation.
Red-team your own scope file. Take the scope.json from Lab 1 and write five "attacker" requests (HTTP requests, CLI commands) that try to violate it — a request to an out-of-scope domain, an action that violates the RoE, a request past valid_until, etc. For each, identify which scope field should block it. This is the test suite S01's middleware must pass.
# Lab Specification — Module S00: Legal, Ethics, and Rules of Engagement
**Course**: 2A — Building AI Harnesses for Cybersecurity
**Module**: S00 — Legal, Ethics, and Rules of Engagement
**Duration**: 60–90 minutes (three labs, one per sub-section)
**Environment**: Local. No live targets. A text editor, a JSON validator, and access to public bug bounty policy pages (read-only).
> These labs are deliberately non-technical in the scanning sense. They produce the *legal* artifacts — a scope file, a disclosure template, a retention policy, an authorization checklist — that the rest of the course's engineering builds on. The scope file you produce in Lab 1 is the literal input to S01's scope enforcement middleware. You are converting a contract into a control.
---
## Lab 0 — Environment setup (5 min)
No special tooling. You need:
- A JSON validator (any — `jq`, Python's `json.tool`, an online linter).
- A markdown editor for the templates.
- Browser access to two public bug bounty program policy pages (HackerOne and Bugcrowd — links in the references).
```bash
# Verify jq is available (used to validate your scope file)
jq --version # need 1.6+
```
That is the entire environment. The point of this module is that the legal layer is upstream of the engineering; you do not need a scanner to produce a defensible scope file.
---
## Lab 1 — Formalize a Bug Bounty Scope as a Machine-Checkable JSON Scope File
**Maps to**: S00.1 — Authorization and Scope Law
**Time**: 20–25 minutes
**Output**: `scope.json` — the input to S01's scope enforcement middleware
### Objective
Take a real bug bounty program policy (a contract written in English) and formalize it into a structured scope file that a harness can load and check on every outbound tool call. The discipline of converting prose scope into machine-checkable scope surfaces every ambiguity the prose hides.
### Phase 1 — Read a real program policy
1. Open a real HackerOne program policy (e.g., a well-known public program — pick any with a published policy and safe harbor).
2. Open a real Bugcrowd program policy (any public program).
Read both in full. You are looking for five things, and the prose will not always make them easy to find:
- **In-scope assets** — the exhaustive list of properties for which safe harbor is granted.
- **Out-of-scope / exclusions** — assets that override apparent in-scope matches.
- **Rules of engagement (behavior)** — what you may and may not *do* (rate caps, no DoS, no exfil, etc.).
- **Safe harbor clause** — what it actually covers and what it conditions on.
- **Disclosure policy** — the timeline and vendor-sign-off expectations.
**Record** which of the five each policy makes explicit, and which it leaves ambiguous. The ambiguous ones are where the legal risk concentrates.
### Phase 2 — Formalize into the scope schema
Produce `scope.json` using the schema below. Fill it from ONE of the two policies you read. Be literal — if the policy is ambiguous, mark the field `null` and note it; do not invent a value.
```json
{
"engagement_id": "h1-<program-name>-2026-07",
"schema_version": "1.0.0",
"generated_at": "2026-07-08T12:00:00Z",
"valid_until": "2026-07-15T00:00:00Z",
"asset_owner": {
"name": "<the company that owns the assets>",
"authorization_basis": "<'direct program' | 'contractor-warranted' | unknown>"
},
"program_sponsor": {
"name": "<the program operator, if different from owner>",
"warrants_owner_authorization": true
},
"in_scope": [
{ "type": "domain", "value": "app.example.com", "match": "exact" },
{ "type": "domain", "value": "api.example.com", "match": "exact" }
],
"out_of_scope": [
{ "type": "domain", "value": "status.example.com", "reason": "third-party status page" },
{ "type": "domain_pattern", "value": "*.thirdparty-cdn.net", "reason": "third-party infrastructure" }
],
"rules_of_engagement": {
"max_concurrent_connections": 10,
"max_requests_per_second": 20,
"forbidden_actions": ["dos", "social_engineering", "data_exfiltration", "physical_testing"],
"minimum_proof_only": true,
"stop_after_validation": true
},
"safe_harbor": {
"granted": true,
"conditions": ["in_scope_only", "roe_compliant_only"],
"covers_third_parties": false
},
"disclosure": {
"policy": "CVD",
"coordination_window_days": 45,
"requires_vendor_signoff_for_publication": true
}
}
```
### Phase 3 — Validate and stress-test
1. Validate the JSON: `jq . scope.json > /dev/null` (silent success = valid).
2. **Stress-test your scope file** by answering these questions against it. Each is a real authorization-chain question:
- A redirect points to `cdn.example.com`. Is it in scope? (Does your `in_scope` cover it? Is it in `out_of_scope`?)
- The target rate-limits at 100 req/s. What does your harness scan at? (Should be ≤ your `max_requests_per_second`, which should be ≤ the target's tolerance.)
- You found a valid AWS key on an in-scope asset. May you use it to enumerate buckets? (Check `forbidden_actions` — `data_exfiltration` should block this.)
- The engagement is now day 8. Is your scope still valid? (Check `valid_until`.)
- A third party on shared infrastructure complains about your scanning. Does safe harbor cover you? (Check `covers_third_parties` — it should be `false`.)
### Deliverable
- [ ] `scope.json` valid against the schema, filled from a real policy
- [ ] A one-paragraph note on which of the five policy elements were explicit vs. ambiguous in the program you chose
- [ ] Answers to the five stress-test questions, each citing the scope field that resolves it
### Solution key (what a correct scope file demonstrates)
- `in_scope` is an **exhaustive list** with explicit `match` semantics (`exact`, not implied wildcard), per the Disclose.io/HackerOne/Bugcrowd standard.
- `out_of_scope` is checked **after** `in_scope` matching — a domain in both lists is out of scope.
- `rules_of_engagement.forbidden_actions` includes at minimum `dos`, `data_exfiltration`, `social_engineering`.
- `max_requests_per_second` is a real number sourced from the policy (or a conservative default if unspecified — never unset).
- `valid_until` is a real timestamp, not null. Scope is re-verified each session.
- `safe_harbor.covers_third_parties` is `false` — this is the honest reading of every program's safe harbor.
- The five stress-test answers each cite a specific field. A correct Lab 1 demonstrates that a prose policy has been converted into a binary, machine-checkable boundary — which is exactly what S01's middleware loads.
---
## Lab 2 — Write a CVD Timeline Template and an Evidence Retention Policy
**Maps to**: S00.2 — Responsible Disclosure and Evidence Obligations
**Time**: 20 minutes
**Output**: `disclosure-template.md` and `retention-policy.md`
### Phase 1 — The disclosure timeline template
Write `disclosure-template.md` — a reusable timeline template a harness can stamp onto every finding. It must contain the minimum fields that make disclosure auditable:
```markdown
# Coordinated Vulnerability Disclosure — Finding [ID]
## Timeline
| Date (UTC) | Event | Actor | Notes |
| --- | --- | --- | --- |
| YYYY-MM-DD | Discovery | Tester | Scope ref: <scope_ref> |
| YYYY-MM-DD | Private report submitted | Tester | Channel: <H1/Bugcrowd/email> |
| YYYY-MM-DD | Vendor acknowledgment expected | Vendor | Window: 7 days from report |
| YYYY-MM-DD | Fix expected (coordination window) | Vendor | ~45 days CERT/CC default (or program window) |
| YYYY-MM-DD | Coordinated public disclosure | Both | Requires vendor signoff: YES |
## Disclosure conditions
- [ ] Vendor has acknowledged
- [ ] Vendor has fixed OR coordination window has elapsed with vendor silence
- [ ] Vendor has signed off on public disclosure (if required)
- [ ] Proof redacted of any sensitive data before publication
## If vendor is unresponsive
Do NOT publish before the coordination window elapses. Early disclosure can void safe harbor.
```
Fill one instance of this template for a hypothetical finding (make up the dates and details). The point is to feel which fields the harness must auto-populate and which require human decision.
### Phase 2 — The evidence retention policy
Write `retention-policy.md`. Define the four retention classes, their retention period, and their destruction step. This is the policy the harness's evidence logger enforces.
```markdown
# Evidence Retention Policy
## Classification (applied at capture)
| Class | Definition | Retention | Destruction |
| --- | --- | --- | --- |
| Public | No sensitive data; non-exploitable artifact | Indefinite (portfolio) | N/A |
| Redacted | Sensitive data redacted at capture time | Engagement window + 90-day tail | Automated purge after tail |
| Restricted | Contains real PII, credentials, or proprietary data | Until report submission (or per contract) | Destroy on report; record the deletion |
| Destroyed | (Post-destruction record) | Indefinitely (the record of deletion, not the data) | N/A |
## Capture rules
- Default to NOT capturing. If unsure, classify Restricted and destroy early.
- Minimum proof only: COUNT(*) or 1 redacted row, never SELECT *.
- Credentials/secrets: report the exposure, redact in report, securely delete the captured key.
## Enforcement
The evidence logger classifies each record at capture and enforces retention automatically. A record classified Restricted is destroyed on report submission; the destruction is logged (we keep the record of the deletion, not the deleted data).
```
### Deliverable
- [ ] `disclosure-template.md` filled for one hypothetical finding, with all condition checkboxes present
- [ ] `retention-policy.md` with all four classes defined, retention periods, and destruction steps
- [ ] One sentence on which fields in the disclosure template the harness auto-populates vs. which require human decision
### Solution key
- Disclosure template must include: discovery date, report date, vendor ack window, fix/coordination window (45-day default or program-specified), publication date, and a "do not publish before window" guard.
- Retention policy must include all four classes with distinct retention periods. **Restricted destroys on report submission** (not "indefinitely"). The destruction is itself recorded.
- Auto-populated fields: discovery date (from evidence timestamp), scope_ref, report submission date. Human-decision fields: agreed publication date, vendor signoff status, whether a finding is ready for public disclosure.
---
## Lab 3 — Authorization Checklist and Legal Disclaimer (Pre-Engagement Gate)
**Maps to**: S00.3 — Harness-Specific Legal Risks
**Time**: 15–20 minutes
**Output**: `pre-engagement-checklist.md` — runs before every harness engagement
### Objective
Produce a pre-engagement authorization checklist that a human (or a gate node in the harness) runs before every engagement. Every item maps to a specific risk from S00.3. If any item fails, the engagement does not start.
### Phase 1 — Write the checklist
```markdown
# Pre-Engagement Authorization Checklist
## Engagement: [name] · Date: [YYYY-MM-DD]
### Authorization chain (S00.1)
- [ ] Asset owner identified and named
- [ ] Program sponsor warrants owner authorization (or = owner)
- [ ] Scope file present, validated (jq), and valid_until > now
- [ ] Scope file re-verified this session (not stale)
### Rules of engagement (S00.1)
- [ ] RoE rate caps present in scope file
- [ ] Forbidden actions list includes: dos, data_exfiltration, social_engineering
- [ ] minimum_proof_only = true
- [ ] Autonomy level set to ≤ 3 for production engagement
### Scope enforcement (S00.3 Risk 2)
- [ ] Scope enforcement middleware active
- [ ] Every outbound tool call passes through it (verified by a dry-run block)
- [ ] Scope_ref stamping confirmed on a test call
### Evidence handling (S00.2 + S00.3 Risk 3)
- [ ] Evidence logger active, append-only, scope-referenced
- [ ] Retention policy loaded (Public/Redacted/Restricted/Destroyed)
- [ ] Restricted-class destruction-on-report configured
### DoS / scanning controls (S00.3 Risk 1)
- [ ] Rate-limit middleware active on all scanning tools
- [ ] Distress-detection (5xx/reset/latency) configured as a stop condition
- [ ] Default concurrency set low
### Agentic escalation controls (S00.3 Risk 4)
- [ ] Kill switch tested (harness stops within 1 second)
- [ ] Blast radius bounded: single session cannot exceed one engagement's scope
- [ ] Credential scope capped (no host credential store access)
- [ ] Audit log captures scope_ref per call
### Disclosure readiness (S00.2)
- [ ] CVD timeline template loaded
- [ ] Program disclosure window known (45-day default or program-specified)
## Gate
If ANY unchecked: DO NOT START THE ENGAGEMENT. Fix the gap first.
```
### Phase 2 — The legal disclaimer
Append a short disclaimer that ships with every engagement's output. This is not a substitute for legal review, but it establishes the engagement's basis explicitly:
```markdown
## Authorization basis
This engagement is conducted under the authorization of [asset owner], as specified
in the scope file [scope.json, engagement_id]. Testing is limited to in-scope assets
and rules of engagement defined therein. All findings are produced under coordinated
vulnerability disclosure. This document is an engineering record, not legal advice;
for the specific engagement, authorization was reviewed on [date].
```
### Deliverable
- [ ] `pre-engagement-checklist.md` complete, with every risk from S00.3 mapped to at least one item
- [ ] For each of the four S00.3 risks, identify which checklist section gates it (write this mapping as a comment at the top of the file)
- [ ] Legal disclaimer present and filled for a hypothetical engagement
### Solution key
- The risk-to-section mapping: Risk 1 (DoS) → "DoS / scanning controls"; Risk 2 (out-of-scope) → "Scope enforcement"; Risk 3 (evidence retention) → "Evidence handling"; Risk 4 (agentic escalation) → "Agentic escalation controls" + the autonomy check under "Rules of engagement."
- A correct checklist is **fail-closed**: any unchecked item blocks engagement start. The gate line is the load-bearing element.
- The scope enforcement "dry-run block" verification is critical — it proves the middleware actually intercepts calls, not just that the config file says it does.
---
## Stretch goals
1. **Write a scope-file linter.** A small script (`jq` or Python) that takes `scope.json` and fails if: any required field is null, `valid_until` is in the past, `forbidden_actions` is missing any of the four required entries, or `in_scope` is empty. This is the CI gate for scope files — scope files that fail the linter do not load into a harness.
2. **Model the authorization chain as a state machine.** In Mermaid or in code, model the chain from asset owner to tool call as a state machine where each transition checks a condition. Identify the three breaks (Sponsor≠Owner, action outside RoE, stale scope) as the transitions that can fail. This is a direct warm-up for S01's scope enforcement implementation.
3. **Red-team your own scope file.** Take the `scope.json` from Lab 1 and write five "attacker" requests (HTTP requests, CLI commands) that try to violate it — a request to an out-of-scope domain, an action that violates the RoE, a request past `valid_until`, etc. For each, identify which scope field should block it. This is the test suite S01's middleware must pass.
---
## References
- HackerOne Safe Harbor framework: https://docs.hackerone.com/en/articles/8494502-safe-harbor-overview-faq
- Bugcrowd / Disclose.io safe harbor: https://docs.bugcrowd.com/researchers/disclosure/disclose-io-and-safe-harbor/
- CERT/CC vulnerability disclosure guidance (45-day coordination window)
- 18 U.S.C. § 1030 (CFAA); *Van Buren v. United States*, 593 U.S. 374 (2021)
- Computer Misuse Act 1990 (UK), ss. 1, 3
- Directive 2013/40/EU; GDPR (Reg. 2016/679)