Exploration Artifact Specification

Definition

An Exploration is a concrete, reproducible security experiment that tests a Scenario hypothesis. Every exploration includes both a Baseline (legitimate path) and an Attack path to enable Delta Analysis - comparing what happens when invariants are enforced versus when an attacker attempts to violate them.

Explorations transform theoretical scenarios into empirical evidence. They answer: "What actually happens when we try this attack?"

Key principle: Delta Analysis is the heart of exploration. By comparing baseline and attack paths side-by-side, we definitively determine whether security controls work as expected.

Schema: exploration.schema.yaml


Required Fields

FieldTypeDescription
idExplorationIDUnique identifier E-<SYSTEM>-<NAME>-<NNN>-<VV>
titlestringDescriptive name of the experiment
scenario_refScenarioIDReference to the Scenario being tested
objectivestringWhat the experiment aims to confirm/refute
base_setupPathSetupSetup for baseline (legitimate) path
base_actionPathActionSteps executed in baseline path
base_expectedPathExpectedExpected observations for baseline
att_setupPathSetupSetup for attack path
att_actionPathActionSteps executed in attack path
att_expectedPathExpectedExpected observations for attack
observationsObservationsActual results from execution
verdictExplorationVerdictFinal determination (VIOLATION_CONFIRMED, ENFORCEMENT_CONFIRMED, INCONCLUSIVE, or BLOCKED)

Optional Fields

FieldTypeDefaultDescription
setupExplorationSetup-Shared setup (env, fixtures, bindings)
trace_refsTraceRefs-Optional links to baseline/attack Trace artifacts (recommended for temporal claims)
acceptance_criteriaAcceptanceCriteria-Pass/fail definitions
cleanupCleanup-Teardown instructions
statusArtifactStatusDRAFTLifecycle status
versionstring-Semantic version

Delta Analysis: BASE vs ATT

The core of exploration is Delta Analysis - comparing two parallel paths:

Baseline (BASE)

The legitimate path where invariants should hold:

  • Actor is authorized
  • Parameters are valid
  • Sequence is correct
  • Expected: Success with proper authorization

Attack (ATT)

The manipulated path where invariants should block (or be violated):

  • Actor may be unauthorized
  • Parameters may be manipulated
  • Sequence may be disrupted
  • Expected (if secure): Rejection with appropriate error
  • Observed (if vulnerable): Unauthorized success

Delta Comparison

AspectBaselineAttackIndicates
Status Code200 OK403 ForbiddenEnforced - attack blocked
Status Code200 OK200 OKViolated - attack succeeded
Resource StateModified by ownerUnchangedEnforced
Resource StateModified by ownerModified by attackerViolated
Logs"user.deleted_own_item""unauthorized_delete_attempt"Enforced
Logs"user.deleted_own_item""user.deleted_own_item"Violated (if attacker)

Field Details

base_setup and att_setup

Each path has its own setup establishing context:

yaml
base_setup:
  description: "Alice (owner) prepares to delete her own item"
  actor: "Alice"
  context: "Authenticated as alice@example.com with valid session"
 
att_setup:
  description: "Bob (attacker) prepares to delete Alice's item"
  actor: "Bob"
  context: "Authenticated as bob@example.com, using Alice's item_id"
  prerequisites:
    - "Obtained Alice's item_id through enumeration"

base_action and att_action

Actions are ordered steps with concrete commands:

yaml
base_action:
  goal: "Owner successfully deletes their own item"
  steps:
    - order: 1
      description: "Alice deletes her item"
      command: |
        curl -X DELETE "${BASE}/api/items/${alice_item_id}" \
          -H "Authorization: Bearer ${alice_token}"
      request:
        method: DELETE
        path: "/api/items/${alice_item_id}"
        headers:
          Authorization: "Bearer ${alice_token}"
 
att_action:
  goal: "Attacker attempts to delete another user's item"
  steps:
    - order: 1
      description: "Bob attempts to delete Alice's item"
      command: |
        curl -X DELETE "${BASE}/api/items/${alice_item_id}" \
          -H "Authorization: Bearer ${bob_token}"
      request:
        method: DELETE
        path: "/api/items/${alice_item_id}"
        headers:
          Authorization: "Bearer ${bob_token}"

base_expected and att_expected

Document what should happen in each path:

yaml
base_expected:
  outcome: "Alice successfully deletes her item"
  status_code: 200
  response_contains:
    - '"deleted": true'
  invariants:
    - invariant: INV-SHOP-OWNER-DELETE
      expected_state: HOLDS
      notes: "Owner deleting own resource - should succeed"
  state_changes:
    - "Item no longer exists in database"
 
att_expected:
  outcome: "Bob's attempt is rejected"
  status_code: 403
  response_contains:
    - '"error"'
    - "Forbidden"
  invariants:
    - invariant: INV-SHOP-OWNER-DELETE
      expected_state: HOLDS
      notes: "Non-owner blocked from deleting - invariant enforced"
  state_changes:
    - "Item remains intact in database"

observations

Filled after execution:

yaml
observations:
  executed_at: "2025-01-15T10:30:00Z"
  environment: "local-docker"
  base_results:
    success: true
    status_code: 200
    response_body: '{"deleted": true, "itemId": "item-123"}'
    state_after:
      item_exists: false
  att_results:
    success: true  # This is bad!
    status_code: 200
    response_body: '{"deleted": true, "itemId": "item-123"}'
    state_after:
      item_exists: false
  delta:
    summary: "Attack succeeded - no difference from baseline"
    differences:
      - aspect: "Authorization check"
        baseline: "Owner identity validated"
        attack: "No owner check performed"
        significance: "CRITICAL - anyone can delete any item"
    invariant_states:
      - invariant: INV-SHOP-OWNER-DELETE
        state: VIOLATED
        evidence: "Bob deleted Alice's item with 200 OK"

trace_refs (optional)

When temporal operators are used (or ordering is central to the claim), link to structured Trace artifacts that capture the baseline and/or attack event sequence.

yaml
trace_refs:
  baseline: T-SHOP-IDOR-DELETE-001-01-01
  attack: T-SHOP-IDOR-DELETE-001-01-02

verdict

The final determination:

ResultMeaning
VIOLATION_CONFIRMEDAttack succeeded, invariant was violated
ENFORCEMENT_CONFIRMEDAttack was blocked, invariant held
INCONCLUSIVECould not determine (errors, ambiguous results)
BLOCKEDExploration could not be executed
yaml
verdict:
  result: VIOLATION_CONFIRMED
  summary: >
    The INV-SHOP-OWNER-DELETE invariant is violated. Bob successfully
    deleted Alice's item without authorization. The API only checks
    authentication, not authorization.
  confidence: HIGH
  finding_ref: FD-SHOP-AUTHZ-001
  recommendations:
    - "Add ownership check in delete endpoint"
    - "Review all resource operations for similar IDOR"

Key Patterns

  • Always run both paths. Without baseline, you can't confirm the attack path is actually different behavior.

  • Same environment. Both paths must execute in identical conditions to make delta meaningful.

  • Concrete commands. Use actual curl/httpie commands that can be copy-pasted and reproduced.

  • Extract and reuse values. Capture tokens, IDs, and responses for use in subsequent steps.

  • Document everything. Actual responses, timestamps, and state changes are evidence.


Common Mistakes

MistakeProblemFix
[ ] No baseline pathCannot prove attack behavior is abnormalAlways include baseline for comparison
[ ] Vague commandsNot reproducibleUse exact curl commands with all headers
[ ] Missing observationsNo evidence for verdictFill observations after execution
[ ] Premature verdictClaiming result before testingOnly set verdict after actual execution
[ ] Different environmentsDelta not meaningfulRun both paths in same environment
[ ] No cleanupState pollution affects other testsDocument teardown steps

Example: E-SHOP-IDOR-DELETE-001-01

yaml
id: E-SHOP-IDOR-DELETE-001-01
title: "IDOR Delete Attack: Bob Deletes Alice's Item"
 
scenario_ref: S-SHOP-IDOR-DELETE-001
 
objective: >
  Confirm whether INV-SHOP-OWNER-DELETE is enforced when Bob attempts
  to delete Alice's item using the DELETE /api/items/{id} endpoint.
 
setup:
  env:
    BASE: "http://localhost:3000"
  preconditions:
    - "Application running on localhost:3000"
    - "Database seeded with test data"
  fixtures:
    - name: alice
      type: user
      data:
        email: "alice@example.com"
        password: "password123"
    - name: bob
      type: user
      data:
        email: "bob@example.com"
        password: "password456"
    - name: alice_item
      type: item
      data:
        name: "Alice's Laptop"
        price: 999
        owner: "${alice.id}"
  bindings:
    - role: owner
      user: alice
      token: alice_token
    - role: attacker
      user: bob
      token: bob_token
 
base_setup:
  description: "Alice (owner) prepares to delete her own item"
  actor: "Alice"
  context: "Authenticated as alice@example.com"
  prerequisites:
    - "Login as Alice to obtain token"
 
base_action:
  goal: "Owner successfully deletes their own item"
  steps:
    - order: 1
      description: "Alice logs in"
      command: |
        curl -X POST "${BASE}/api/auth/login" \
          -H "Content-Type: application/json" \
          -d '{"email":"alice@example.com","password":"password123"}'
      extract:
        - name: alice_token
          path: ".token"
          description: "Alice's authentication token"
        - name: alice_id
          path: ".userId"
          description: "Alice's user ID"
 
    - order: 2
      description: "Get Alice's item ID"
      command: |
        curl -X GET "${BASE}/api/items?owner=${alice_id}" \
          -H "Authorization: Bearer ${alice_token}"
      extract:
        - name: alice_item_id
          path: ".items[0].id"
          description: "Alice's item ID"
 
    - order: 3
      description: "Alice deletes her item"
      interaction: I-SHOP-DELETE-ITEM-001
      command: |
        curl -X DELETE "${BASE}/api/items/${alice_item_id}" \
          -H "Authorization: Bearer ${alice_token}"
      request:
        method: DELETE
        path: "/api/items/${alice_item_id}"
        headers:
          Authorization: "Bearer ${alice_token}"
 
base_expected:
  outcome: "Alice successfully deletes her own item"
  status_code: 200
  response_contains:
    - '"deleted": true'
  invariants:
    - invariant: INV-SHOP-OWNER-DELETE
      expected_state: HOLDS
      notes: "Owner deleting own resource - should succeed"
  state_changes:
    - "Alice's item no longer exists in database"
 
att_setup:
  description: "Bob (attacker) prepares to delete Alice's item"
  actor: "Bob"
  context: "Authenticated as bob@example.com, targeting Alice's item"
  prerequisites:
    - "Bob has Alice's item_id (obtained through enumeration)"
    - "Fresh item created for Alice (to avoid test pollution)"
 
att_action:
  goal: "Attacker attempts to delete another user's item"
  steps:
    - order: 1
      description: "Bob logs in"
      command: |
        curl -X POST "${BASE}/api/auth/login" \
          -H "Content-Type: application/json" \
          -d '{"email":"bob@example.com","password":"password456"}'
      extract:
        - name: bob_token
          path: ".token"
          description: "Bob's authentication token"
 
    - order: 2
      description: "Create fresh item for Alice"
      command: |
        curl -X POST "${BASE}/api/items" \
          -H "Authorization: Bearer ${alice_token}" \
          -H "Content-Type: application/json" \
          -d '{"name":"Target Item","price":100}'
      extract:
        - name: target_item_id
          path: ".id"
          description: "Alice's item that Bob will target"
 
    - order: 3
      description: "Bob attempts to delete Alice's item"
      interaction: I-SHOP-DELETE-ITEM-001
      command: |
        curl -X DELETE "${BASE}/api/items/${target_item_id}" \
          -H "Authorization: Bearer ${bob_token}"
      request:
        method: DELETE
        path: "/api/items/${target_item_id}"
        headers:
          Authorization: "Bearer ${bob_token}"
      notes: "This should fail with 403 if INV-SHOP-OWNER-DELETE is enforced"
 
att_expected:
  outcome: "Bob's attempt is rejected with 403 Forbidden"
  status_code: 403
  response_contains:
    - '"error"'
    - "Forbidden"
    - "not authorized"
  invariants:
    - invariant: INV-SHOP-OWNER-DELETE
      expected_state: HOLDS
      notes: "Non-owner should be blocked from deleting"
  state_changes:
    - "Alice's item remains intact in database"
 
observations:
  executed_at: "2025-01-15T10:30:00Z"
  environment: "local-docker (shop-app:latest)"
  base_results:
    success: true
    status_code: 200
    response_body: '{"deleted":true,"itemId":"item-abc123"}'
    state_after:
      item_exists: false
    logs:
      - "[INFO] item.deleted user=alice item=item-abc123"
  att_results:
    success: true
    status_code: 200
    response_body: '{"deleted":true,"itemId":"item-xyz789"}'
    state_after:
      item_exists: false
    logs:
      - "[INFO] item.deleted user=bob item=item-xyz789"
    errors: []
  delta:
    summary: >
      CRITICAL: Attack path succeeded identically to baseline.
      Bob deleted Alice's item with 200 OK. No ownership check performed.
    differences:
      - aspect: "HTTP Response"
        baseline: "200 OK"
        attack: "200 OK"
        significance: "Attack should have returned 403"
      - aspect: "Resource State"
        baseline: "Item deleted (expected - owner)"
        attack: "Item deleted (VULNERABILITY - non-owner)"
        significance: "Unauthorized deletion succeeded"
      - aspect: "Audit Log"
        baseline: "item.deleted user=alice"
        attack: "item.deleted user=bob"
        significance: "No authorization failure logged"
    invariant_states:
      - invariant: INV-SHOP-OWNER-DELETE
        state: VIOLATED
        evidence: "Bob (non-owner) successfully deleted Alice's item"
 
verdict:
  result: VIOLATION_CONFIRMED
  summary: >
    INV-SHOP-OWNER-DELETE is VIOLATED. The delete endpoint only checks
    authentication (valid token) but not authorization (ownership).
    Any authenticated user can delete any item by knowing its ID.
  confidence: HIGH
  finding_ref: FD-SHOP-AUTHZ-001
  recommendations:
    - "Add ownership check: if (item.ownerId !== user.id) return 403"
    - "Audit all resource endpoints for similar IDOR patterns"
    - "Add integration tests for authorization boundaries"
 
acceptance_criteria:
  pass:
    - "Attack returns 200 OK (same as baseline)"
    - "Item deleted by non-owner"
    - "No authorization error in logs"
  fail:
    - "Attack returns 403 Forbidden"
    - "Item remains intact"
    - "Unauthorized attempt logged"
 
cleanup:
  description: "Remove test users and items"
  steps:
    - "DELETE FROM items WHERE name LIKE 'Target Item%'"
    - "DELETE FROM users WHERE email IN ('alice@example.com', 'bob@example.com')"
  automated: true
 
status: ACTIVE
version: "1.0.0"

Validation Criteria

A valid exploration artifact MUST:

  1. Have a unique ID matching pattern E-<SYSTEM>-<NAME>-<NNN>-<VV>
  2. Reference a valid scenario (S-*)
  3. Have complete base_setup, base_action, base_expected for baseline path
  4. Have complete att_setup, att_action, att_expected for attack path
  5. Have observations section (even if empty before execution)
  6. Have verdict section with result and summary
  7. Use concrete, reproducible commands in action steps
  8. Include invariant expectations tied to scenario's targeted invariants


Common Pitfalls

  • Omitting a baseline run (BASE) so you can’t interpret the attack delta (ATT).
  • Changing multiple variables between BASE and ATT (violates Delta Analysis discipline).
  • Recording outcomes without reproducible evidence (response codes, payload diffs, logs, traces).