Skip to main content

api_scanner/discovery/
mod.rs

1pub mod common_paths;
2pub mod headers;
3pub mod js;
4pub mod robots;
5pub mod sitemap;
6pub mod swagger;
7
8use std::collections::HashSet;
9
10/// Normalise a raw path candidate into a clean `/foo/bar` string.
11/// Returns `None` if the path is empty, crosses origins, or is unusable.
12pub fn normalize_path(raw: &str, target_host: &str) -> Option<String> {
13    let raw = raw.trim();
14    if raw.is_empty() {
15        return None;
16    }
17
18    // If it looks like a full URL, validate the host then extract path
19    if raw.starts_with("http://") || raw.starts_with("https://") {
20        if let Ok(parsed) = url::Url::parse(raw) {
21            if parsed.host_str().unwrap_or("") != target_host {
22                return None; // cross-origin
23            }
24            return normalize_path(parsed.path(), target_host);
25        }
26        return None;
27    }
28
29    let mut path = raw.to_string();
30    if !path.starts_with('/') {
31        path = format!("/{path}");
32    }
33
34    // Strip trailing slash (except root)
35    if path.len() > 1 && path.ends_with('/') {
36        path.pop();
37    }
38
39    Some(path)
40}
41
42/// Keywords that flag a path as interesting for API discovery
43pub fn is_interesting(path: &str) -> bool {
44    const KEYWORDS: &[&str] = &[
45        "api", "graphql", "swagger", "openapi", "admin", "internal", "private", "debug",
46        "actuator", "metrics", "health", "config", "oauth", "auth", "token", "session", "keys",
47        "secret", "rest", "v1", "v2", "v3", "webhook", "upload", "download",
48    ];
49    let lower = path.to_lowercase();
50    KEYWORDS.iter().any(|k| lower.contains(k))
51}
52
53/// Merge a batch of raw path strings into a set, normalising each one
54pub fn collect_paths(
55    raws: impl IntoIterator<Item = String>,
56    host: &str,
57    out: &mut HashSet<String>,
58) {
59    for raw in raws {
60        if let Some(p) = normalize_path(&raw, host) {
61            out.insert(p);
62        }
63    }
64}