# 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)
