Skip to main content

termivar_scanner/defense/
mod.rs

1//! Observation-only defensive posture layer.
2//!
3//! ## Runtime scope
4//!
5//! - **Build:** always/default.
6//! - **Execution:** observation/shadow composition is assessment-only. The
7//!   standalone standard runtime remains unchanged; assessment enforcement is
8//!   an explicit opt-in and defaults off.
9//! - **Default `termivar scan`:** no.
10//! - **Support:** implemented and tested.
11//!
12//! See `docs/internals/runtime-map.md`.
13//!
14//! This module turns raw response signals into a typed, bounded observation of a
15//! target's defensive behavior — product fingerprints and an overall
16//! [`DefenseState`]. It never selects a payload or an evasion technique: that
17//! decision belongs to the planner, which consumes these observations. This
18//! separation is deliberate, so a defensive-fingerprint change can never silently
19//! change attack behavior.
20//!
21//! The former legacy WAF detector/evasion utility has been removed. Payload
22//! derivation lives behind [`crate::payload_strategies`].
23
24pub mod enforcement;
25pub mod fingerprint;
26pub mod policy;
27pub mod projection;
28pub mod shadow_planning;
29pub mod state;
30pub mod transition;
31
32pub use enforcement::{defense_aware_plan, DefensePlanningPolicy};
33pub use fingerprint::{
34    fingerprint, DefenseFingerprint, DefenseProduct, FingerprintConfidence,
35    MAX_FINGERPRINT_BODY_SCAN_BYTES,
36};
37pub use policy::{recommend, DefenseResponse};
38pub use projection::{
39    project_defense_state, project_defense_transition, project_outcome, DefenseObservationContext,
40    ObservedOutcome,
41};
42pub use shadow_planning::{
43    decide, defense_aware_shadow_plan, explanation_code, render_explanation,
44    DefenseAwareShadowPlan, DefenseInteractionClass, InteractionDecision, PlanAdjustment,
45    ResourceDefenseObservation, ResourceDefenseSignal, ShadowPlanDelta, SuppressedAction,
46};
47pub use state::{DefensePosture, DefenseState, DefenseStatusSignal};
48pub use transition::{DefenseTransition, DefenseTransitionKind, PostureShift};
49
50/// Applies the monotonic defense decision table at the assessment-composition
51/// boundary. This adapter cannot add actions or increase their intensity.
52#[cfg(feature = "scanning")]
53pub(crate) fn assessment_interaction_decision(
54    response: DefenseResponse,
55    class: DefenseInteractionClass,
56) -> InteractionDecision {
57    shadow_planning::decide(response, class)
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn observation_layer_reexports_compose() {
66        // The re-exported surface is enough to observe a response end to end
67        // without reaching into submodules.
68        let state = DefenseState::observe(
69            403,
70            &[("Server", "cloudflare"), ("CF-RAY", "abc")],
71            "Attention Required!",
72        );
73        assert_eq!(state.posture(), DefensePosture::Blocking);
74        let print: &DefenseFingerprint = state.fingerprint().unwrap();
75        assert_eq!(print.product(), DefenseProduct::Cloudflare);
76        assert_eq!(print.confidence(), FingerprintConfidence::Strong);
77    }
78}