Classroom Glossary Public page

Week 5: Tool Safety Engineering I (Tool v0.2, authz + dry-run)

1,957 words

The week where the tool gets dangerous, and the safety controls that make that acceptable. Tool v0.2 adds the CSRF reproduction; --authorized-by becomes required; --dry-run is the default cognitive check before any state change.


Theme

Tool v0.1 was a non-destructive probe. Tool v0.2 adds the CSRF reproduction itself: when invoked correctly, it issues the GET to the SB6141 destructive endpoint, causing the modem to factory-reset. This is the first version of the tool that can do something that changes the world. The safety controls that gate the action are what make it acceptable that the tool can do this at all.

Two new affordances arrive in v0.2:

  • --authorized-by NAME is REQUIRED. The argparse parser exits with error: --authorized-by is required if absent. The flag carries the name of the authorizing party; the value is logged on every action. The flag is not a checkbox; the flag is an annotation that survives in the audit trail.

  • --dry-run prints what the tool WOULD do without doing it. Default is OFF; explicit --dry-run is ON. A dry-run output is exact (same target, same URL, same headers) but stops short of issuing the destructive request. The discipline is to ALWAYS dry-run first; the human who reviews the dry-run output is the safety check.

The week's lecture frames these as instances of the broader category Schneier names in Chapter 9 of A Hacker's Mind: high-cost-of-failure systems require multiple independent signals before consequential action. The two-person rule in nuclear-weapons control is the canonical example; the airline-pilot checklist is another; the dry-run / authorization-flag pair in adversarial-research tooling is the engineering-discipline analog.

By the end of Week 5 you have Tool v0.2: it fingerprints (v0.1 behavior), it requires --authorized-by, it defaults to print-only output (with --dry-run as the explicit affirmation), and when invoked with --no-dry-run plus a destructive-action confirmation prompt, it issues the CSRF and demonstrates the SB6141 reset.

Schneier weave (~285 words, A Hacker's Mind Ch 10)

Schneier devotes Chapter 10 of A Hacker's Mind to the failure modes of safety controls. His examples include nuclear-launch-code rotation (when codes are never rotated, they leak), checklist creep (when checklists grow to 200 items, operators stop reading them), and confirmation fatigue (when "are you sure?" prompts appear too often, operators click through them). The pattern across all three is the same: safety controls degrade when their cost grows or their frequency reduces operator attention.

The implication for tool engineering is that safety controls have to be DESIGNED for the operator's working state, not for an idealized careful state. A --authorized-by flag that the operator can shell-alias away is no safety control. A --dry-run mode the operator skips because "I already know what it does" is no safety control. A confirmation prompt the operator dismisses without reading is no safety control.

The Tool v0.2 design choices respond to these failure modes. --authorized-by is argparse-required, not optional; an alias that omits it does not start. The dry-run is the DEFAULT; the operator must explicitly say --no-dry-run to execute, and that explicit affirmation IS the safety control. The destructive-action confirmation prints the full target + action + authorization-line and waits for the operator to type the action name (not just y); typing the action name forces a moment of attention.

Schneier's broader argument is that safety controls are not just code; they are workflow patterns the code embodies. A well-designed safety control is one the operator cannot mindlessly bypass without DELIBERATELY bypassing. Tool v0.2 is the smallest expression of that principle in adversarial-research tooling; v0.3 (Week 7) extends it with structured logging and idempotency; v1.0 (capstone) ships it as a Python package any operator could read and verify.

Reading list (~1 hour)

  1. Schneier, A Hacker's Mind, Ch 10 ("Hacking Hardening"). Academy library; calibre id 677.
  2. Schneier, A Hacker's Mind, Ch 9 (revisited). The original safety-systems framing; revisit now with v0.2's specific design choices in mind.
  3. Atul Gawande, The Checklist Manifesto, Ch 2 ("The Checklist") at any library (not in academy library). The Boeing 707 first-flight checklist is the canonical industrial-strength safety-control example.
  4. Pete Blaber, The Mission, The Men, and Me, Ch 1-2 (decision-making under uncertainty). Academy library; calibre id 691. Operator-decision-doctrine sub-anchor; informs the Disclosure-Ethics Sidebar's OODA-loop reference.
  5. OffSec OSCP study guide on tool-safety conventions (informal; the OffSec community maintains conventions; see practitioner blogs).
  6. Black Hat Python Ch 9 (tool-engineering patterns). Academy library; calibre id 138.

Lecture outline (~50 min)

Part 1: The argparse contract for safety (15 min)

  • required=True on --authorized-by. The argparse parser exits at startup if the flag is absent. The argparse default error message is sufficient; no need for custom handling. Operators learn after one mistake.
  • The value goes everywhere. Every log entry includes the authorized-by string. The dry-run output includes it. The destructive-action confirmation prompt includes it. The post-action summary includes it. If the operator's name does not appear in the audit trail, the audit trail is incomplete.
  • Why a STRING and not a flag. A boolean flag (--authorized) is too easy: alias to --authorized and the alias-author has bypassed the safety control. A required string (--authorized-by "Alice (cohort 2026-A)") is a value the operator types each time; the typing IS the affirmation.
  • What the string carries. The cohort authorization document; the per-session lab-notebook line; the time and target of the action. The string is the operator's signature for THIS run.

Part 2: Dry-run as the default (15 min)

  • --dry-run as the default; --no-dry-run as the explicit affirmation. The argparse pattern:
    parser.add_argument(
        '--dry-run',
        action=argparse.BooleanOptionalAction,
        default=True,
        help='print what the tool would do without doing it (default: %(default)s; use --no-dry-run to execute)',
    )
    
    BooleanOptionalAction is the argparse facility (Python 3.9+) that auto-generates --dry-run and --no-dry-run from one declaration; the user explicitly chooses.
  • What dry-run output includes. The exact HTTP request the tool would issue: method, full URL, headers (including any User-Agent), body if any, the expected response. The point is that the operator can copy the dry-run output into a CVD report and have the complete reproduction record.
  • What dry-run output does NOT include. Inferred or guessed values. If the tool computes a request from arguments + defaults + fingerprint, the dry-run shows the COMPUTED request, not an abstract template.
  • Dry-run vs --check vs --print. Naming matters. --check could mean "verify the target is reachable"; --print could mean "print help and exit." --dry-run is the unambiguous convention from make, Ansible, Terraform, and a dozen other tools.

Part 3: The destructive-action confirmation (10 min)

  • When --no-dry-run is passed. The tool has been told to execute. Even so, the destructive action is gated behind a final interactive confirmation:
    print(f'\n*** About to issue destructive action ***')
    print(f'  Target:       {args.target}')
    print(f'  Action:       factory-reset (POST /goform/RgFactoryDefault)')
    print(f'  Authorized:   {args.authorized_by}')
    print(f'  Time:         {datetime.now(timezone.utc).isoformat()}')
    print(f'')
    expected = 'factory-reset'
    user_input = input(f'Type "{expected}" to confirm (anything else aborts): ').strip()
    if user_input != expected:
        print('aborted', file=sys.stderr)
        sys.exit(EXIT_USER_ABORT)
    
  • Why typing the action name. Forces a moment of attention. y is too easy; factory-reset is hard to mistype.
  • When confirmation can be skipped. With --no-confirm AND --authorized-by AND --no-dry-run all three present. The combination is intentionally awkward; an unattended-CI use case can compose it; an interactive operator typically does not.

Part 4: Operator-decision doctrine sidebar (Blaber + OODA) (10 min)

Pete Blaber's account in The Mission, The Men, and Me of decision-making under uncertainty under operational pressure is the security-research analog the course pulls in here. Blaber's "decision cycle" is John Boyd's OODA loop (Observe, Orient, Decide, Act) applied at small-unit scale: the operator observes the situation, orients themselves to the context, decides what to do, acts. Most failures, Blaber argues, are failures of orientation (you saw the data but did not understand what it meant) or decision (you saw and understood but chose the wrong action under pressure).

Tool v0.2's safety controls are workflow-level OODA enforcement. The dry-run output is the "Observe" stage forced into a deliberate pause. The --authorized-by annotation forces "Orient" (who am I doing this for; under what authority). The destructive-action confirmation forces "Decide" (do I act, or abort). The action itself is "Act." Skipping any stage is the failure mode; v0.2 makes each stage syntactically required.

The Boyd-Blaber framing matters because adversarial security work happens under cognitive load: long lab session; tired operator; deadline pressure; instinct says "just run the tool." The safety controls exist for that operator on that night. The discipline is the discipline.

Disclosure-Ethics Sidebar

The safety controls intersect with norm systems in specific ways:

Norm system This week What the safety controls do
Responsible disclosure The CVE was disclosed in 2015; reproduction in 2026 is academic The --authorized-by annotation in the run log is the contemporaneous record that the reproduction was academic
Academic ethics Repeated factory-resets of the lab modem are within scope The dry-run default reduces the rate of needless resets (modem wear; operator fatigue)
Legal authorization CFAA + state-law authorization applies The --authorized-by value in the audit trail is the per-run evidence; the cohort document is the umbrella authorization

If a future CFAA prosecution somehow targeted academy-cohort SB6141 reproduction work (hypothetical; the academy authorization is documented), the per-action --authorized-by annotations in the JSON logs would be the operator's defense. The discipline of having that artifact is the discipline of operating defensibly.

Labs (~3 hr)

Lab 5: Tool v0.2, Authorization + Dry-Run (labs/lab-5-tool-v02.md)

  • Goal: extend Tool v0.1 to Tool v0.2: adds --authorized-by (REQUIRED), --dry-run (default ON), destructive-action confirmation, the CSRF reproduction itself
  • Time: ~3 hr
  • Artifact: lab-5/sb6141_csrf/v02.py + tests + updated README in ~/adv-101/lab-5/

Independent practice (~5 hr)

  1. Read three real-world incident reports where a tool was misused (1.5 hr). Examples: the 2014 Knight Capital trading-algorithm incident; any major cloud-provider "deletion script wiped the production database" public post-mortem (Gitlab 2017 db-1 incident, AWS S3 outage); a security-tool friendly-fire incident. Notice the common theme: the tool DID what it was told; the human told it to do the wrong thing. v0.2's safety controls exist for this class.
  2. Build a unittest.mock-based test for the confirmation prompt (1.5 hr). The input() builtin can be patched. Write a test where the operator's input is "factory-reset"; expect the tool proceeds. Write a second test where the input is "y"; expect the tool aborts.
  3. Read Pete Blaber Ch 3-5 (1 hr). Continues the decision-under-uncertainty framing; cited in Week 11 capstone preparation.
  4. Schneier A Hacker's Mind Ch 11 (45 min). Hacking-the-Hardening; the meta-question of how safety controls themselves become attack surface.
  5. Cross-tool comparison (45 min). Pick a destructive-action open-source tool (ansible, terraform, helm, kubectl, aws-cli). Look at its --dry-run (or equivalent) behavior. How does the design compare to v0.2's? What did the open-source tool do well? What did it do badly?

Reflection prompts (~30 min)

  1. Your v0.2 requires --authorized-by. Suppose an operator shells alias sb6141-csrf-v02='sb6141-csrf-v02 --authorized-by "test"' to skip typing. Has the safety control been defeated? Defend your answer.
  2. The dry-run is the default. Suppose the operator finds this annoying ("I want to run for real most of the time"). What is the right response? Change the default? Add a --no-confirm-no-dry-run shortcut? Tell the operator to type more?
  3. The destructive-action confirmation requires the operator to type factory-reset. Why not just yes? Why not just y?
  4. Blaber's account of OODA-loop failures emphasizes "orient" failures: you saw the data but did not understand it. What is the v0.2-tool equivalent of an "orient" failure? Sketch a scenario.
  5. One thing from this week you want to know more about?

Adversary Diary (Week 5)

New entries:

  • argparse.BooleanOptionalAction at https://docs.python.org/3/library/argparse.html#argparse.BooleanOptionalAction. Auto-generates --flag / --no-flag from one declaration.
  • unittest.mock.patch('builtins.input') for testing interactive prompts.
  • Operator-decision-doctrine references. Boyd's OODA-loop; Blaber's The Mission, The Men, and Me (calibre id 691); Luberisse A Boydian Approach to Mastering Unconventional Warfare (calibre id 692).
  • Friendly-fire incident registry (informal; major-cloud-provider post-mortems are the contemporary equivalent of nuclear-weapons-incident reports).

What would a reviewer ask?

  1. "Show me what happens when I run your tool without --authorized-by. What is the message? Can I bypass it?"
  2. "Walk me through the dry-run output. Could a reproducing researcher use it as the basis for a CVD report?"
  3. "The destructive-action confirmation requires typing factory-reset. What design choice did you reject? Defend the choice you made."

What comes next

Week 6 is the midterm: a 3-hour proctored practical reproducing a DIFFERENT CVE on a DIFFERENT lab target (academy-provided VM or container; not the SB6141). The reproduction is manual (Burp + curl); no tool engineering is required. The midterm assesses the methodology Weeks 1-5 built: read the CVE; set up the environment; reproduce; document.