termivar_scanner/
web_actions.rs1use serde::{Deserialize, Serialize};
17
18mod native_review;
19
20#[cfg(all(feature = "scanning", feature = "authorization-review"))]
21pub(crate) use native_review::authorization_review_phase_terminal_predicate;
22#[cfg(all(feature = "scanning", feature = "openapi-review"))]
23pub(crate) use native_review::openapi_review_phase_terminal_predicate;
24#[cfg(feature = "scanning")]
25pub(crate) use native_review::{
26 native_web_review_response_marker_predicate, NATIVE_WEB_REVIEW_EVIDENCE_NAMESPACE,
27 NATIVE_WEB_REVIEW_RESPONSE_MARKER,
28};
29#[cfg(all(feature = "scanning", feature = "rest-review"))]
30pub(crate) use native_review::{
31 rest_review_catalog_ready_predicate, rest_review_phase_terminal_predicate,
32};
33#[cfg(all(feature = "scanning", feature = "ssrf-oast-review"))]
34pub(crate) use native_review::{
35 ssrf_oast_review_candidate_ready_predicate, ssrf_oast_review_phase_terminal_predicate,
36};
37pub use native_review::{
38 NativeWebReviewActionKind, NativeWebReviewDifferentialInput, NativeWebReviewRequestLeg,
39 NATIVE_WEB_REVIEW_ACTION_COUNT, NATIVE_WEB_REVIEW_ACTIVE_REQUESTS_PER_CASE,
40 NATIVE_WEB_REVIEW_REQUESTS_PER_CASE,
41};
42
43pub const STANDARD_WEB_ACTION_COUNT: usize = 9;
45
46pub const STANDARD_WEB_DISCOVERY_ACTION_COUNT: usize = 8;
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
51#[serde(rename_all = "snake_case")]
52#[non_exhaustive]
53pub enum StandardWebActionKind {
54 NginxConfiguration,
56 ApacheConfiguration,
58 PhpInputDiscovery,
60 LaravelRouteDiscovery,
62 LaravelInputAnalysis,
64 LivewireComponentDiscovery,
66 SanctumAuthBoundary,
71 HttpBasicAuthBoundary,
73 HttpBearerAuthBoundary,
75}
76
77impl StandardWebActionKind {
78 pub const fn all() -> [Self; STANDARD_WEB_ACTION_COUNT] {
80 [
81 Self::NginxConfiguration,
82 Self::ApacheConfiguration,
83 Self::PhpInputDiscovery,
84 Self::LaravelRouteDiscovery,
85 Self::LaravelInputAnalysis,
86 Self::LivewireComponentDiscovery,
87 Self::SanctumAuthBoundary,
88 Self::HttpBasicAuthBoundary,
89 Self::HttpBearerAuthBoundary,
90 ]
91 }
92
93 pub const fn discovery() -> [Self; STANDARD_WEB_DISCOVERY_ACTION_COUNT] {
99 [
100 Self::NginxConfiguration,
101 Self::ApacheConfiguration,
102 Self::PhpInputDiscovery,
103 Self::LaravelRouteDiscovery,
104 Self::LivewireComponentDiscovery,
105 Self::SanctumAuthBoundary,
106 Self::HttpBasicAuthBoundary,
107 Self::HttpBearerAuthBoundary,
108 ]
109 }
110
111 pub const fn action_id(self) -> &'static str {
113 match self {
114 Self::NginxConfiguration => "web.action.nginx.configuration",
115 Self::ApacheConfiguration => "web.action.apache.configuration",
116 Self::PhpInputDiscovery => "web.action.php.input-discovery",
117 Self::LaravelRouteDiscovery => "web.action.laravel.route-discovery",
118 Self::LaravelInputAnalysis => "web.action.laravel.input-analysis",
119 Self::LivewireComponentDiscovery => "web.action.livewire.component-discovery",
120 Self::SanctumAuthBoundary => "web.action.sanctum.auth-boundary",
121 Self::HttpBasicAuthBoundary => "web.action.http-basic.auth-boundary",
122 Self::HttpBearerAuthBoundary => "web.action.http-bearer.auth-boundary",
123 }
124 }
125
126 pub const fn executor_id(self) -> &'static str {
131 match self {
132 Self::NginxConfiguration => "web.probe.nginx-configuration",
133 Self::ApacheConfiguration => "web.probe.apache-configuration",
134 Self::PhpInputDiscovery => "web.probe.php-inputs",
135 Self::LaravelRouteDiscovery => "web.probe.laravel-routes",
136 Self::LaravelInputAnalysis => "web.probe.laravel-inputs",
137 Self::LivewireComponentDiscovery => "web.probe.livewire-components",
138 Self::SanctumAuthBoundary => "web.probe.sanctum-auth",
139 Self::HttpBasicAuthBoundary => "web.probe.http-basic-auth",
140 Self::HttpBearerAuthBoundary => "web.probe.http-bearer-auth",
141 }
142 }
143
144 pub const fn slug(self) -> &'static str {
151 match self {
152 Self::NginxConfiguration => "nginx-configuration",
153 Self::ApacheConfiguration => "apache-configuration",
154 Self::PhpInputDiscovery => "php-input-discovery",
155 Self::LaravelRouteDiscovery => "laravel-route",
156 Self::LaravelInputAnalysis => "laravel-input-analysis",
157 Self::LivewireComponentDiscovery => "livewire-component",
158 Self::SanctumAuthBoundary => "sanctum-auth",
159 Self::HttpBasicAuthBoundary => "http-basic-auth",
160 Self::HttpBearerAuthBoundary => "http-bearer-auth",
161 }
162 }
163}
164
165pub const STANDARD_WEB_DISCOVERY_ACTIONS: [StandardWebActionKind;
167 STANDARD_WEB_DISCOVERY_ACTION_COUNT] = StandardWebActionKind::discovery();
168
169#[cfg(test)]
170mod tests {
171 use std::collections::BTreeSet;
172
173 use super::*;
174
175 #[test]
176 fn discovery_capability_is_a_stable_subset_of_the_catalog() {
177 assert_eq!(
178 STANDARD_WEB_DISCOVERY_ACTIONS.len(),
179 STANDARD_WEB_DISCOVERY_ACTION_COUNT
180 );
181 for discovery in STANDARD_WEB_DISCOVERY_ACTIONS {
182 assert!(StandardWebActionKind::all().contains(&discovery));
183 }
184 }
185
186 #[test]
187 fn every_standard_action_has_a_unique_stable_slug() {
188 let slugs = StandardWebActionKind::all()
189 .into_iter()
190 .map(StandardWebActionKind::slug)
191 .collect::<BTreeSet<_>>();
192
193 assert_eq!(slugs.len(), STANDARD_WEB_ACTION_COUNT);
194 assert!(slugs.iter().all(|slug| !slug.is_empty()));
195 assert_eq!(
196 StandardWebActionKind::LaravelRouteDiscovery.slug(),
197 "laravel-route"
198 );
199 assert_eq!(
200 StandardWebActionKind::HttpBearerAuthBoundary.slug(),
201 "http-bearer-auth"
202 );
203 }
204}