Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions docs/adr/0001-standalone-rust-gateway-workspace-core.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# ADR 0001: Standalone Rust gateway with in-workspace `waf-ids-core`

- Status: Accepted
- Date: 2026-08-25
- Recorded from: current `main` (`README.md` workspace notes;
`docs/architecture.md` components and security boundaries; root
`Cargo.toml` workspace members)

## Context

wardnet (crate name `waf-ids-ai-soc`) is the WAF / IDS / AI SOC gateway
and control-plane leaf for ContextualWisdomLab. Operators need a binary
that starts, serves management and gateway HTTP, and scores requests
without checking out sibling products.

Domain logic (models, validation, upserts, scoring, DNSBL zone text,
event retention, feed freshness, KPI snapshots) is reusable. Splitting
that logic into a git submodule before an independently versioned
engine or SDK exists would add release and review overhead without an
external consumer.

Cargo workspaces keep multiple packages on one lockfile and one
`cargo test --workspace` surface (The Cargo Book, n.d.).

## Decision

1. Ship a **standalone Rust gateway**. The process runs by itself with
optional operator configuration. No sibling checkout is required.
2. Keep reusable domain code in **`crates/waf-ids-core`**, a member of
the same Cargo workspace (`path` dependency), not a git submodule,
until an independently versioned engine, SDK, or adapter needs its
own release lifecycle.
3. Treat sibling ContextualWisdomLab products as **optional composition
callers** over HTTP or documented contracts:
- **naruon** and **gyeot** may call or be called when an operator
wires them; they are not required tree members.
- **contextual-orchestrator** may front the optional SOC LLM
(`SOC_LLM_BASE_URL`); absent configuration, SOC assist is off.
- **Clearfolio** may receive optional document-viewer relays
(`CLEARFOLIO_BASE_URL`); absent configuration, that surface stays
disabled.
Existing optional caller links stay in place. Do not require those
services to start the gateway.

## Consequences

- Operators can `cargo run` and use `/admin`, `/gateway/{path}`, and
`/dnsbl/zone` on a single binary.
- `waf-ids-core` stays free of async/HTTP dependencies so domain tests
and fuzz mirrors do not pull the Axum crate graph.
- A later submodule or crates.io publish is deferred until a real
second consumer and release cadence exist.
- Optional Clearfolio and orchestrator hooks must remain inert when
unconfigured so the leaf stays independently runnable.

## References

The Cargo Book. (n.d.). *Workspaces*.
https://doc.rust-lang.org/cargo/reference/workspaces.html
55 changes: 55 additions & 0 deletions docs/adr/0002-optional-json-state-standalone-durability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# ADR 0002: Optional JSON state for standalone durability

- Status: Accepted
- Date: 2026-08-25
- Recorded from: current `main` (`README.md` run notes;
`docs/architecture.md` security boundaries; `docs/runbooks/operations.md`
persistence behavior)

## Context

The gateway must keep operator-managed routes, threat indicators, DNSBL
entries, events, and license metadata across a local restart when an
operator asks for durability. Many lab and smoke runs do not need a
file at all.

JSON is the Internet Standard data interchange format for this class of
text documents (Bray, 2017, RFC 8259 / STD 90). A single pretty-printed
object is enough for a standalone process. It is not a multi-operator
database, a backup system, or an audited change workflow.

## Decision

1. `WAF_IDS_STATE_PATH` is **optional**. When unset, the process uses
seeded in-memory state. Health reports `persistence: memory`.
2. When the path is set, load JSON from that file (or seed and create
it). Persist with a **temporary sibling file** and **atomic rename**
onto the configured path. Health reports `persistence: file`.
3. If a management write cannot replace the state file, **roll back**
the in-memory mutation and return an operator-visible error.
4. Treat this JSON file as **baseline standalone durability only**. It
is not a production control-plane database, not a backup plan, and
not an audited change-management system.

A production database is **not** an accepted architecture decision on
current `main`.

## Consequences

- `scripts/smoke.sh` can prove restart persistence with a temporary
JSON file and no external datastore.
- Parse failures on a configured path fail startup rather than silently
ignoring a corrupt file (`docs/security/threat-model.md`).
- Concurrent writers and disaster recovery remain out of scope until a
later durable store is accepted.
- Schema evolution is the application's JSON shape, not a migration
framework.

## References

Bray, T. (Ed.). (2017). *The JavaScript Object Notation (JSON) data
interchange format* (RFC 8259). RFC Editor.
https://doi.org/10.17487/RFC8259

*(Internet Standard, STD 90. Live-checked 2026-08-25 via
https://www.rfc-editor.org/info/rfc8259 and the DOI above.)*
57 changes: 57 additions & 0 deletions docs/adr/0003-owasp-crs-coraza-waf-authority.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# ADR 0003: OWASP CRS / Coraza as WAF authority

- Status: Accepted
- Date: 2026-08-25
- Recorded from: current `main` (`README.md` production-coverage note;
`docs/architecture.md` near-term WAF integration)

## Context

The gateway scores requests from local threat indicators and DNSBL
entries. That baseline is not a replacement for a maintained WAF rule
set. Inventing an in-house rule language would duplicate work the
OWASP Core Rule Set already does for generic attack detection
(OWASP Foundation / CRS Project, n.d.).

OWASP Coraza is an open-source WAF engine documented as compatible
with OWASP CRS (OWASP Coraza, n.d.). Current `main` already accepts
Coraza/CRS **audit** documents; it does not embed Coraza in-process.

## Decision

1. **OWASP CRS remains the WAF rule authority.** Do not replace CRS
with hand-rolled gateway rules.
2. Accept admin-authenticated Coraza / OWASP CRS **audit JSON/NDJSON**
at `POST /api/waf/coraza/audit`. Interrupted transactions and CRS
rule messages become `SecurityEvent` rows. Block-grade hits may seed
DNSBL and `client_ip` / path threat indicators so later gateway
decisions can enforce matching clients.
3. Run Coraza **outside** this process for now. **In-process Coraza
embedding is a follow-up**, not an accepted replacement of CRS and
not an accepted replacement of the audit ingest path.

Related accepted ingest on the same `main` (IDS, not WAF authority):
admin-authenticated Suricata EVE JSON/NDJSON at
`POST /api/ids/suricata/eve` (Eve JSON output, n.d.). Full route
correlation and live EVE tailing remain follow-ups.

## Consequences

- Operators can attach an external Coraza/CRS deployment and still use
this gateway for scoring, events, and route-scoped block mode.
- CRS versioning and rule quality stay with the CRS project (latest
line observed 2026-08-25: 4.29.0 on https://coreruleset.org/).
- Embedding Coraza later must still consume CRS; it must not become a
pretext for a parallel hand-written rule pack.

## References

OWASP Coraza. (n.d.). *Documentation*. https://coraza.io/docs/

OWASP Coraza. (n.d.). *OWASP Coraza WAF*. https://coraza.io/

OWASP Foundation / CRS Project. (n.d.). *OWASP Core Rule Set*.
https://coreruleset.org/

Eve JSON output. (n.d.). In *Suricata documentation*.
https://docs.suricata.io/en/latest/output/eve/eve-json-output.html
69 changes: 69 additions & 0 deletions docs/adr/0004-rfc-5782-style-dnsbl-zone-export.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# ADR 0004: RFC 5782-style DNSBL zone export

- Status: Accepted
- Date: 2026-08-25
- Recorded from: current `main` (`README.md` DNSBL notes;
`docs/architecture.md` `/dnsbl/zone` and DNSBL serving follow-up;
`docs/fuzzing.md` zone-export invariants)

## Context

Operators publish listed addresses so mail and gateway scorers can
query a DNS blacklist. Levine (2010) describes DNS blacklists and
whitelists as **IRTF Informational** practice: this is **not** an
Internet Standards Track specification. The conventional listing
record is an A resource record whose address is a **response code**,
not a destination to connect to. Those A values SHOULD lie in
`127.0.0.0/8` so a mistaken use as an IP address stays on loopback
(Levine, 2010, RFC 5782).

Zone and resource-record structure follows DNS concepts and the DNS
implementation specification (Mockapetris, 1987a, RFC 1034 / STD 13;
Mockapetris, 1987b, RFC 1035 / STD 13).

## Decision

1. Export an **RFC 5782-style DNSBL zone** at `GET /dnsbl/zone` using
the configured `DNSBL_ORIGIN`. The `dnsbl.local` default is for local
development only; authoritative deployments must explicitly configure a
non-`.local` origin because `.local.` is reserved for mDNS.
2. Require every published DNSBL **response code** to be an IPv4
loopback-style address in **`127.0.0.0/8`**. Reject codes outside
that range at the management API.
3. Treat the export as **zone text suitable for an authoritative DNS
server**. This process does **not** serve DNS on port 53.
4. **Hickory DNS authoritative serving is a follow-up**, to be
considered after zone-export semantics stabilize. It is not
accepted on current `main`.

## Consequences

- Management upserts stay keyed by listed `address`; the A-record
payload is the validated `127.0.0.0/8` code.
- Fuzz and property tests require every published A-record code to
remain a loopback literal and every TXT payload to stay escaped
(`docs/fuzzing.md`).
- RFC 5782 remains Informational. Local validation is stricter
(`MUST` in this gateway) than the RFC `SHOULD` on A values.
- IPv6 DNSxL layout in RFC 5782 is not an accepted serving mode here.

## References

Cheshire, S., & Krochmal, M. (2013). *Multicast DNS* (RFC 6762).
RFC Editor. https://doi.org/10.17487/RFC6762

Levine, J. (2010). *DNS blacklists and whitelists* (RFC 5782). RFC
Editor. https://doi.org/10.17487/RFC5782

*(IRTF Informational; not Standards Track. Also
https://www.rfc-editor.org/info/rfc5782. Live-checked 2026-08-25.)*

Mockapetris, P. (1987a). *Domain names—concepts and facilities*
(RFC 1034). RFC Editor. https://doi.org/10.17487/RFC1034

*(Internet Standard, STD 13.)*

Mockapetris, P. (1987b). *Domain names—implementation and
specification* (RFC 1035). RFC Editor. https://doi.org/10.17487/RFC1035

*(Internet Standard, STD 13.)*
73 changes: 73 additions & 0 deletions docs/adr/0005-coverage-guided-fuzzing-untrusted-inputs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# ADR 0005: Coverage-guided fuzzing of untrusted-input surfaces

- Status: Accepted
- Date: 2026-08-25
- Recorded from: current `main` (`docs/fuzzing.md`; README verification
note; in-repo preprint PDF
`docs/papers/fuzzing-art-science-engineering-survey-arxiv-1812.00140.pdf`)

## Context

The gateway parses attacker-controlled path, query, body, and client
IP on every request. Startup also deserializes untrusted JSON state
and admin-token configuration. DNSBL zone generation emits text from
operator-supplied reasons and codes.

Manès et al. (2021) survey fuzzing as repeated execution with
generated, often malformed inputs, and treat coverage-guided fuzzing
as a primary engineering method for finding crashes and invariant
violations on those surfaces. This ADR cites that **published** IEEE
Transactions on Software Engineering article as primary. The 2018
arXiv posting is the **preprint** of the same work (Manès et al.,
2018) and is already vendored in-repo. No other fuzzing paper is
cited.

## Decision

1. Exercise the untrusted-input surfaces with **coverage-guided
fuzzing** (cargo-fuzz / libFuzzer) on nightly:
- `fuzz_score_request` — `waf_ids_core::score_request`
- `fuzz_appdata_json` — `AppData` state-file JSON
- `fuzz_parse_admin_tokens` — admin-token configuration parser
- `fuzz_dnsbl_zone` — DNSBL zone export / validation
2. Keep a **stable property-test mirror** (`proptest`) in
`crates/waf-ids-core/tests/fuzz_invariants.rs` and
`tests/fuzz_invariants.rs` so the same invariants run on stable in
`cargo test --workspace`.
3. Isolate fuzz targets in the `fuzz/` Cargo workspace so root
`cargo test --workspace` never builds libFuzzer targets.
4. When an untrusted-input surface changes, keep the libFuzzer target
and the property-test mirror in sync (`docs/fuzzing.md`).

## Consequences

- Invariants include: no panic on arbitrary valid UTF-8 JSON input for the
state parser; non-empty score
reasons; deterministic scoring; serde round-trip of parsed state;
no empty token key or empty actor; TXT payloads fully escaped;
every published A-record code in `127.0.0.0/8`.
- Pull-request CI smoke-fuzzes each target for a bounded budget;
nightly runs a longer budget. Those workflows are operational, not
additional papers.
- Coverage-gate stubs and cancelled scanner runs are not evidence for
this decision.

## References

Manès, V. J. M., Han, H., Han, C., Cha, S. K., Egele, M., Schwartz,
E. J., & Woo, M. (2021). The art, science, and engineering of
fuzzing: A survey. *IEEE Transactions on Software Engineering,
47*(11), 2312–2331. https://doi.org/10.1109/TSE.2019.2946563

*(Primary published version. Crossref record confirmed 2026-08-25:
title, volume 47 issue 11, pages 2312–2331, date 2021-11-01. DOI
resolver reached `https://ieeexplore.ieee.org/document/8863940/`.)*

Manès, V. J. M., Han, H., Han, C., Cha, S. K., Egele, M., Schwartz,
E. J., & Woo, M. (2018). The art, science, and engineering of
fuzzing: A survey. *arXiv*.
https://doi.org/10.48550/arXiv.1812.00140

*(Preprint; arXiv:1812.00140. Submitted 2018-12-01, revised 2019-04-08
as v4. Local copy:
`docs/papers/fuzzing-art-science-engineering-survey-arxiv-1812.00140.pdf`.)*
76 changes: 76 additions & 0 deletions docs/adr/0006-admin-token-threat-intel-document-ingest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# ADR 0006: Admin-token threat-intel document ingest

- Status: Accepted
- Date: 2026-08-25
- Recorded from: current `main` (`docs/architecture.md` threat
intelligence paragraph; admin console copy for the ingest routes)

## Context

Gateway scoring needs threat indicators and DNSBL entries. Operators
already hold documents from STIX/TAXII, MISP, and OpenCTI. Those
documents are untrusted until validated. Live pull jobs against a
MISP REST API or an OpenCTI GraphQL endpoint are a different
operational surface (credentials, scheduling, pagination) and are
**not** accepted on current `main`.

STIX 2.1 is an OASIS Standard for exchanging cyber threat
intelligence objects (Jordan et al., 2021a). TAXII 2.1 is the OASIS
Standard for transporting STIX over HTTP collections (Jordan &
Varner, 2021). MISP is an open threat-intelligence sharing platform
and associated open standards (MISP Project, n.d.; MISP Standard,
n.d.). OpenCTI documents an open CTI platform with official
operator documentation (OpenCTI, n.d.).

## Decision

1. Ingest **operator-posted documents** on admin-authenticated routes:
- `POST /api/threat-intel/stix` — STIX 2.x indicator or bundle JSON
- `POST /api/threat-intel/misp` — MISP Event / attribute JSON
(`to_ids=false` attributes skipped)
- `POST /api/threat-intel/opencti` — OpenCTI observable / indicator
export JSON
Comment thread
seonghobae marked this conversation as resolved.
2. Support operator-initiated remote TAXII ingestion at
`POST /api/threat-intel/taxii/poll`: receive a TAXII 2.1 objects URL
(or API root plus collection id) and optional credentials, fetch the
external endpoint, normalize its response to STIX, then upsert it.
3. Map supported IP, domain, URL, and hash material into
`ThreatIndicator` and `DnsblEntry` rows and update feed freshness.
4. **Live MISP REST pull** and **live OpenCTI GraphQL pull** remain
follow-ups. They are not accepted replacements for document ingest.
5. Never write TAXII or admin credentials into audit-log payloads.

## Consequences

- An operator (or an external poller they control) can push reviewed
intelligence without this process holding a standing MISP or
OpenCTI session.
- STIX/TAXII citations are the OASIS Standard HTML editions fetched
2026-08-25, not drafts.
- MISP Internet-Draft HTML for a “core format” exists on
misp-standard.org; this ADR does **not** treat that draft as a
published RFC or Standards Track document. The accepted references
are the official project and standard landings.
- TAXII poll still performs an outbound HTTP GET of objects the
operator named; that is document transport, not a live MISP/OpenCTI
product puller.

## References

Jordan, B., Piazza, R., & Darley, T. (Eds.). (2021, June 10). *STIX
Version 2.1* (OASIS Standard). OASIS Open.
https://docs.oasis-open.org/cti/stix/v2.1/os/stix-v2.1-os.html

Jordan, B., & Varner, D. (Eds.). (2021, June 10). *TAXII Version 2.1*
(OASIS Standard). OASIS Open.
https://docs.oasis-open.org/cti/taxii/v2.1/os/taxii-v2.1-os.html

MISP Project. (n.d.). *MISP open source threat intelligence platform
& open standards for threat intelligence sharing*.
https://www.misp-project.org/

MISP Standard. (n.d.). *MISP standard*.
https://www.misp-standard.org/

OpenCTI. (n.d.). *OpenCTI documentation*.
https://docs.opencti.io/latest/
Loading
Loading