## Contents

- 8. Safe Testing Practices
- Rules of Engagement
- Safe-test controls for side-effecting attacks
- Test Environment Setup

## 8. Safe Testing Practices

### Rules of Engagement

```
DO:
  ✓ Only test applications you own or have written authorization to test
  ✓ Use staging/test environments, never production
  ✓ Create dedicated test accounts with known credentials
  ✓ Set scope rules to avoid destructive endpoints
  ✓ Review reports before sharing (may contain sensitive data)
  ✓ Keep API keys secure (Shannon uses significant API credits)

DON'T:
  ✗ Point Shannon at production systems
  ✗ Test third-party services without explicit written permission
  ✗ Share reports containing valid credentials or PII
  ✗ Run without scope rules on apps with destructive endpoints
  ✗ Ignore the cost — monitor API spend during runs
```

### Safe-test controls for side-effecting attacks

Some attack classes have real-world blast radius even in staging. Apply these controls *before* enabling them — and prefer the scope `avoid` rules in §5 when in doubt.

| Attack class | Hazard | Required controls |
|--------------|--------|-------------------|
| **Brute force / password spraying** | Locks accounts; floods auth; triggers WAF/SIEM alerts | Use disposable accounts you can re-create; cap attempts per account *below* the lockout threshold (e.g. 3 if lockout is 5); cap requests/sec (`limits.max_requests_per_second`); raise or disable lockout for the dedicated test users only; never spray real usernames |
| **Credential stuffing** | Lateral lockouts; alerts the real users whose emails are tried | Test ONLY against seeded fake accounts in an isolated DB; never load a real breach corpus against a shared environment; disable any "new device" email on the test tenant |
| **2FA / OTP bypass & enumeration** | Burns SMS/email budget; spams real recipients; locks 2FA | Use TOTP test secrets you control (not SMS) — see the `totp:` block in §2; if SMS/email is unavoidable, route it to a catch-all mailbox / SMS sandbox and rate-limit; never enumerate against real phone numbers |
| **SSRF / cloud-metadata probing** | Can pivot into real internal services or live cloud creds | Run only in an isolated network with NO route to production VPCs or `169.254.169.254`; in cloud CI, enforce IMDSv2 and scope the runner's IAM role to nothing; assert the metadata endpoint is unreachable from the test host before probing |
| **Email / SMS / push triggers** (signup, reset, invite, notify) | Real messages to real people; sender-reputation damage | Add `/api/notifications/**`, invite, and reset flows to `avoid`, OR point the test env's mail/SMS provider at a sandbox (e.g. a catch-all inbox); verify NODE_ENV/test config routes nothing to the real provider |
| **Payment / billing / refund endpoints** | Real charges, refunds, payouts, webhooks | Always `avoid` these unless the env uses the payment provider's *test mode* keys with test cards; assert the publishable key is a test key before running; never test billing against live keys |

**Pre-run assertions (fail closed).** Bake these checks into the test harness so a misconfigured target aborts the run instead of doing damage:

```bash
# Refuse to run unless we're clearly NOT in production
[ "$NODE_ENV" = "test" ] || { echo "Refusing: NODE_ENV is not 'test'"; exit 1; }
case "$TARGET_URL" in *prod*|*www.*) echo "Refusing: target looks like production"; exit 1;; esac
# Cloud metadata must be unreachable from the test host before SSRF probing
curl -s --max-time 2 http://169.254.169.254/ >/dev/null \
  && { echo "Refusing: cloud metadata endpoint is reachable from test host"; exit 1; } || true
```

### Test Environment Setup

```yaml
# docker-compose.test.yml — isolated test environment
services:
  app:
    build: .
    environment:
      - NODE_ENV=test
      - DATABASE_URL=postgres://test:test@db:5432/testdb
    ports:
      - "3000:3000"
    networks:
      - pentest-net

  db:
    image: postgres:16
    environment:
      - POSTGRES_DB=testdb
      - POSTGRES_USER=test
      - POSTGRES_PASSWORD=test
    networks:
      - pentest-net

networks:
  pentest-net:
    driver: bridge
    # Isolated network — no access to host or internet
```
