Skip to content

Commit

Permalink
Merge pull request #40 from eredotpkfr/39-integration-add-chaos-integ…
Browse files Browse the repository at this point in the history
…ration

Integration - New Chaos Module
  • Loading branch information
eredotpkfr authored Oct 3, 2024
2 parents eafeb26 + 29f7822 commit 7221039
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 11 deletions.
1 change: 1 addition & 0 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ SUBSCAN_BUFFEROVER_APIKEY=baz
SUBSCAN_BUILTWITH_APIKEY=foo
SUBSCAN_CENSYS_APIKEY=bar
SUBSCAN_CERTSPOTTER_APIKEY=baz
SUBSCAN_CHAOS_APIKEY=foo
2 changes: 1 addition & 1 deletion src/bin/subscan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ async fn main() {
let mut module = item.lock().await;
let requester = module.requester().await.unwrap();

if module.name().await != "certspotter" {
if module.name().await != "chaos" {
continue;
}

Expand Down
2 changes: 2 additions & 0 deletions src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
engines::{bing, duckduckgo, google, yahoo},
integrations::{
alienvault, anubis, bevigil, binaryedge, bufferover, builtwith, censys, certspotter,
chaos,
},
},
};
Expand All @@ -29,6 +30,7 @@ lazy_static! {
Mutex::new(builtwith::BuiltWith::dispatcher()),
Mutex::new(censys::Censys::dispatcher()),
Mutex::new(certspotter::CertSpotter::dispatcher()),
Mutex::new(chaos::Chaos::dispatcher()),
];
}

Expand Down
5 changes: 3 additions & 2 deletions src/extractors/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use async_trait::async_trait;
use serde_json;
use std::collections::BTreeSet;

/// JSON content parser wrapper struct. This object compatible
/// with [`SubdomainExtractorInterface`] and it uses `extract`
/// JSON content parser wrapper struct
///
/// This object compatible with [`SubdomainExtractorInterface`] and it uses `extract`
/// method to extract subdomain addresses from JSON content.
/// JSON parsing function must be given for this extractor. Please
/// follow up examples to learn usage techniques
Expand Down
61 changes: 61 additions & 0 deletions src/modules/integrations/chaos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use crate::{
enums::{APIAuthMethod, RequesterDispatcher, SubscanModuleDispatcher},
extractors::json::JSONExtractor,
modules::generics::api_integration::GenericAPIIntegrationModule,
requesters::client::HTTPClient,
types::core::Subdomain,
};
use reqwest::Url;
use serde_json::Value;
use std::collections::BTreeSet;

pub const CHAOS_MODULE_NAME: &str = "chaos";
pub const CHAOS_URL: &str = "https://dns.projectdiscovery.io/dns";

/// `Chaos` API integration module
///
/// It uses [`GenericAPIIntegrationModule`] its own inner
/// here are the configurations
///
/// | Property | Value |
/// |:------------------:|:-----------------------------------:|
/// | Module Name | `chaos` |
/// | Doc URL | <https://cloud.projectdiscovery.io> |
/// | Authentication | [`APIAuthMethod::APIKeyAsHeader`] |
pub struct Chaos {}

impl Chaos {
pub fn dispatcher() -> SubscanModuleDispatcher {
let requester: RequesterDispatcher = HTTPClient::default().into();
let extractor: JSONExtractor = JSONExtractor::new(Box::new(Self::extract));

let generic = GenericAPIIntegrationModule {
name: CHAOS_MODULE_NAME.into(),
url: Box::new(Self::get_query_url),
next: Box::new(Self::get_next_url),
auth: APIAuthMethod::APIKeyAsHeader("Authorization".into()),
requester: requester.into(),
extractor: extractor.into(),
};

generic.into()
}

pub fn get_query_url(domain: &str) -> String {
format!("{CHAOS_URL}/{domain}/subdomains")
}

pub fn get_next_url(_url: Url, _content: Value) -> Option<Url> {
None
}

pub fn extract(content: Value, domain: String) -> BTreeSet<Subdomain> {
if let Some(subs) = content["subdomains"].as_array() {
let filter = |item: &Value| Some(format!("{}.{}", item.as_str()?, domain));

return subs.iter().filter_map(filter).collect();
}

[].into()
}
}
18 changes: 10 additions & 8 deletions src/modules/integrations/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
/// Alienvault API integration module
/// `AlienVault` API integration module
pub mod alienvault;
/// Anubis API integration module
/// `Anubis` API integration module
pub mod anubis;
/// Bevigil API integration module, API key required
/// `Bevigil` API integration module, API key required
pub mod bevigil;
/// Binaryedge API integration mmodule, API key required
/// `BinaryEdge` API integration mmodule, API key required
pub mod binaryedge;
/// Bufferover API integration mmodule, API key required
/// `BufferOver` API integration mmodule, API key required
pub mod bufferover;
/// Builtwith API integration mmodule, API key required
/// `BuiltWith` API integration mmodule, API key required
pub mod builtwith;
/// Censys API integration, basic HTTP auth required but `Authorization`
/// `Censys` API integration, basic HTTP auth required but `Authorization`
/// header can be used (e.g. `Authorization: Basic foo`)
pub mod censys;
/// Certspotter API integration, API key required
/// `CertSpotter` API integration, API key required
pub mod certspotter;
/// Chaos API integration, API key required
pub mod chaos;
45 changes: 45 additions & 0 deletions tests/modules/integrations/chaos_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use crate::common::{
constants::{TEST_BAR_SUBDOMAIN, TEST_DOMAIN},
funcs::read_stub,
mocks,
};
use serde_json::{self, Value};
use std::{collections::BTreeSet, env};
use subscan::{
interfaces::module::SubscanModuleInterface,
modules::integrations::chaos::{self, CHAOS_MODULE_NAME, CHAOS_URL},
};

#[tokio::test]
#[stubr::mock("module/integrations/chaos.json")]
async fn chaos_run_test() {
let mut chaos = chaos::Chaos::dispatcher();
let (env_name, _) = chaos.fetch_apikey().await;

env::set_var(&env_name, "chaos-api-key");
mocks::wrap_module_dispatcher_url_field(&mut chaos, &stubr.path("/chaos"));

let result = chaos.run(TEST_DOMAIN.to_string()).await;

assert_eq!(chaos.name().await, CHAOS_MODULE_NAME);
assert_eq!(result, [TEST_BAR_SUBDOMAIN.to_string()].into());

env::remove_var(env_name);
}

#[tokio::test]
async fn get_query_url_test() {
let url = chaos::Chaos::get_query_url(TEST_DOMAIN);

assert_eq!(url, format!("{CHAOS_URL}/{TEST_DOMAIN}/subdomains"));
}

#[tokio::test]
async fn extract_test() {
let json = read_stub("module/integrations/chaos.json")["response"]["jsonBody"].clone();
let extracted = chaos::Chaos::extract(json, TEST_DOMAIN.to_string());
let not_extracted = chaos::Chaos::extract(Value::Null, TEST_DOMAIN.to_string());

assert_eq!(extracted, [TEST_BAR_SUBDOMAIN.to_string()].into());
assert_eq!(not_extracted, BTreeSet::new());
}
23 changes: 23 additions & 0 deletions tests/stubs/module/integrations/chaos.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"request": {
"headers": {
"Authorization": {
"equalTo": "chaos-api-key"
}
},
"method": "GET",
"urlPath": "/chaos"
},
"response": {
"headers": {
"content-type": "application/json"
},
"jsonBody": {
"domain": "foo.com",
"subdomains": [
"bar"
]
},
"status": 200
}
}

0 comments on commit 7221039

Please sign in to comment.