Tech-stack/CMS fingerprinting with confidence-scored evidence, implementing foundation-web-analysis::Analyzer -- CMS, analytics, chat widget, booking system, JS framework, marketing, and form-tool detection from fetched HTML + response headers
| Tier | infrastructure |
| Role | unclassified (baselined) |
| Path | crates/infrastructure/web-tech-detector |
| Edition | 2021 |
| Targets | infrastructure_web_tech_detector |
| Public items | 42 across 2 modules |
| Tests | 64 |
What it is for
# infrastructure-web-tech-detector
Tech-stack/CMS fingerprinting analyzer implementing foundation_web_analysis::Analyzer. Fetches a page and matches its HTML plus Server/X-Powered-By response headers against known signatures for CMS platforms, JS frameworks, analytics tools, chat widgets, booking systems, marketing tools, and form tools -- each detection carrying a DetectionConfidence (High/Medium/Low) and, for CMS, the evidence strings that produced it. An undetected CMS is always flagged for manual review via ManualReviewItem.
Lifted from plumber/src/analyzers/tech_detector.rs and plumber/src/models/technology_stack.rs (sprint 3.46, wave 2 -- see CHANGELOG.md for provenance).
Capabilities
detector (other)
Technology-stack / CMS fingerprinting analyzer.
| Item |
|---|
pub const DEFAULT_MAX_BYTES: usize |
TechDetector
Technology-stack / CMS fingerprinting analyzer.
| Item |
|---|
pub struct TechDetector |
TechDetector :: fn new() -> std::result::Result <Self, infrastructure_fetcher::SafeHttpError> |
TechDetector :: fn with_max_bytes(mut self, max_bytes : usize) -> Self |
TechDetector :: fn default() -> Self |
TechDetector :: fn name(& self) -> & 'static str |
TechDetector :: async fn analyze(& self, organization_id : Uuid, website_url : & str) -> Result <AnalysisResult> |
TechDetector :: fn requires_link_graph(& self) -> bool |
TechDetector :: fn cache_ttl_days(& self) -> u32 |
AnalyticsSignatures
Technology-stack data model.
| Item |
|---|
pub struct AnalyticsSignatures |
AnalyticsSignatures :: fn google_analytics() -> Vec <& 'static str> |
AnalyticsSignatures :: fn google_tag_manager() -> Vec <& 'static str> |
AnalyticsSignatures :: fn facebook_pixel() -> Vec <& 'static str> |
AnalyticsSignatures :: fn hotjar() -> Vec <& 'static str> |
AnalyticsTool
Technology-stack data model.
| Item |
|---|
pub struct AnalyticsTool |
ChatSignatures
Technology-stack data model.
| Item |
|---|
pub struct ChatSignatures |
ChatSignatures :: fn intercom() -> Vec <& 'static str> |
ChatSignatures :: fn drift() -> Vec <& 'static str> |
ChatSignatures :: fn zendesk() -> Vec <& 'static str> |
ChatSignatures :: fn livechat() -> Vec <& 'static str> |
ChatSignatures :: fn tawk() -> Vec <& 'static str> |
ChatSignatures :: fn hubspot() -> Vec <& 'static str> |
ChatWidget
Technology-stack data model.
| Item |
|---|
pub struct ChatWidget |
CmsDetection
Technology-stack data model.
| Item |
|---|
pub struct CmsDetection |
CmsSignatures
Technology-stack data model.
| Item |
|---|
pub struct CmsSignatures |
CmsSignatures :: fn wordpress() -> Vec <(& 'static str, & 'static str)> |
CmsSignatures :: fn wix() -> Vec <(& 'static str, & 'static str)> |
CmsSignatures :: fn squarespace() -> Vec <(& 'static str, & 'static str)> |
CmsSignatures :: fn shopify() -> Vec <(& 'static str, & 'static str)> |
CmsSignatures :: fn godaddy_website_builder() -> Vec <(& 'static str, & 'static str)> |
CmsSignatures :: fn weebly() -> Vec <(& 'static str, & 'static str)> |
CmsSignatures :: fn joomla() -> Vec <(& 'static str, & 'static str)> |
CmsSignatures :: fn drupal() -> Vec <(& 'static str, & 'static str)> |
DetectionConfidence
Technology-stack data model.
| Item |
|---|
pub enum DetectionConfidence |
DetectionConfidence :: fn fmt(& self, f : & mut std::fmt::Formatter <'_>) -> std::fmt::Result |
DetectionEvidence
Technology-stack data model.
| Item |
|---|
pub struct DetectionEvidence |
EvidenceType
Technology-stack data model.
| Item |
|---|
pub enum EvidenceType |
TechnologyStack
Technology-stack data model.
| Item |
|---|
pub struct TechnologyStack |
TechnologyStack :: fn default() -> Self |
TechnologyStack :: fn new(organization_id : Uuid) -> Self |
TechnologyStack :: fn add_evidence(& mut self, evidence : DetectionEvidence) |
TechnologyStack :: fn uses(& self, tech_name : & str) -> bool |
How to use it
No examples/ target and no doctest in this crate's rustdoc. The tests listed under Verification are the closest executable usage.
Module structure
infrastructure_web_tech_detector
detectormodels
flowchart TD n_infrastructure_web_tech_detector["infrastructure_web_tech_detector"] n_infrastructure_web_tech_detector --> n_detector["detector"] n_infrastructure_web_tech_detector --> n_models["models"]
Public surface
`detector`
| Item | What it is |
|---|---|
pub const DEFAULT_MAX_BYTES: usize | Response body byte cap for a fetched page (Gate 1.5.6 input limit). |
pub struct TechDetector | Analyzer for detecting technology stack (CMS, frameworks, analytics, chat widgets, booking systems, marketing tools, form tools) |
TechDetector :: fn new() -> std::result::Result <Self, infrastructure_fetcher::SafeHttpError> | Build a detector with the default byte cap (DEFAULT_MAX_BYTES) and timeout (infrastructure_fetcher::safe_http::DEFAULT_TIMEOUT, 30s). |
TechDetector :: fn with_max_bytes(mut self, max_bytes : usize) -> Self | Override the response body byte cap. |
TechDetector :: fn default() -> Self | # Panics Panics if the underlying SafeHttpClient cannot be built (TLS backend init failure) -- matches the source's expect-on-construction posture (Client::builder()...expect("Failed to build HTTP client")). |
TechDetector :: fn name(& self) -> & 'static str | — |
TechDetector :: async fn analyze(& self, organization_id : Uuid, website_url : & str) -> Result <AnalysisResult> | — |
TechDetector :: fn requires_link_graph(& self) -> bool | — |
TechDetector :: fn cache_ttl_days(& self) -> u32 | — |
`models`
| Item | What it is |
|---|---|
pub struct TechnologyStack | Detected technology stack for an organization's website. |
pub struct CmsDetection | A detected CMS, with the confidence level and the evidence strings that produced it. |
pub enum DetectionConfidence | How certain a detection is, from the strength/count of matching signals. |
DetectionConfidence :: fn fmt(& self, f : & mut std::fmt::Formatter <'_>) -> std::fmt::Result | — |
pub struct AnalyticsTool | — |
pub struct ChatWidget | — |
pub struct DetectionEvidence | One piece of raw evidence behind a detection -- carried in the model so a future detector pass can attach the specific signal it matched, not just the summary conclusion |
pub enum EvidenceType | — |
TechnologyStack :: fn default() -> Self | — |
TechnologyStack :: fn new(organization_id : Uuid) -> Self | — |
TechnologyStack :: fn add_evidence(& mut self, evidence : DetectionEvidence) | — |
TechnologyStack :: fn uses(& self, tech_name : & str) -> bool | Check whether a named technology was detected anywhere in the stack (CMS name, JS frameworks, analytics tools, or chat widgets), matched case-insensitively as a substring. |
pub struct CmsSignatures | Reference catalog of common CMS signatures, as (location, pattern) pairs |
CmsSignatures :: fn wordpress() -> Vec <(& 'static str, & 'static str)> | — |
CmsSignatures :: fn wix() -> Vec <(& 'static str, & 'static str)> | — |
CmsSignatures :: fn squarespace() -> Vec <(& 'static str, & 'static str)> | — |
CmsSignatures :: fn shopify() -> Vec <(& 'static str, & 'static str)> | — |
CmsSignatures :: fn godaddy_website_builder() -> Vec <(& 'static str, & 'static str)> | — |
CmsSignatures :: fn weebly() -> Vec <(& 'static str, & 'static str)> | — |
CmsSignatures :: fn joomla() -> Vec <(& 'static str, & 'static str)> | — |
CmsSignatures :: fn drupal() -> Vec <(& 'static str, & 'static str)> | — |
pub struct AnalyticsSignatures | Reference catalog of common analytics-tool signatures |
AnalyticsSignatures :: fn google_analytics() -> Vec <& 'static str> | — |
AnalyticsSignatures :: fn google_tag_manager() -> Vec <& 'static str> | — |
AnalyticsSignatures :: fn facebook_pixel() -> Vec <& 'static str> | — |
AnalyticsSignatures :: fn hotjar() -> Vec <& 'static str> | — |
pub struct ChatSignatures | Reference catalog of common chat-widget signatures |
ChatSignatures :: fn intercom() -> Vec <& 'static str> | — |
ChatSignatures :: fn drift() -> Vec <& 'static str> | — |
ChatSignatures :: fn zendesk() -> Vec <& 'static str> | — |
ChatSignatures :: fn livechat() -> Vec <& 'static str> | — |
ChatSignatures :: fn tawk() -> Vec <& 'static str> | — |
ChatSignatures :: fn hubspot() -> Vec <& 'static str> | — |
Re-exports. Exported here, defined elsewhere.
| Export | Defined in |
|---|---|
{AnalyticsSignatures,AnalyticsTool,ChatSignatures,ChatWidget,CmsDetection,CmsSignatures,DetectionConfidence,DetectionEvidence,EvidenceType,TechnologyStack,} | models::{AnalyticsSignatures,AnalyticsTool,ChatSignatures,ChatWidget,CmsDetection,CmsSignatures,DetectionConfidence,DetectionEvidence,EvidenceType,TechnologyStack,} |
{TechDetector,DEFAULT_MAX_BYTES} | detector::{TechDetector,DEFAULT_MAX_BYTES} |
Boundary
Reaches into foundation.
Shares tier infrastructure with 82 other crates: infrastructure-acquire, infrastructure-adapters-google-calendar, infrastructure-adapters-google-gmail, infrastructure-adapters-google-places, infrastructure-adapters-google-trends, infrastructure-adapters-shodan, infrastructure-adapters-yelp, infrastructure-agent, … (82 total).
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | infrastructure |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/infrastructure/web-tech-detector |
| Vocabulary in force (lexicon) | current |
Tier flow. Which tiers this crate's own edges cross.
flowchart LR n_infrastructure["infrastructure"] --> n_foundation["foundation"]
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `foundation-web-analysis` | foundation | no | always |
| `infrastructure-fetcher` | infrastructure | no | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
async-trait | ^0.1 | — | no | always |
chrono | ^0.4 | serde | no | always |
regex | ^1 | — | no | always |
reqwest | ^0.12 | rustls-tls | no | always |
scraper | ^0.20 | — | no | always |
serde | ^1 | derive | no | always |
serde_json | ^1 | — | no | always |
thiserror | ^2 | — | no | always |
url | ^2 | — | no | always |
uuid | ^1 | v4, v7, serde, js | no | always |
Development, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
tokio | ^1 | full, full, test-util | no | always |
tokio-test | ^0.4 | — | no | always |
Build. None.
Depended on by. Nothing in this workspace.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR SELF["infrastructure-web-tech-detector"] SELF -->|runtime| n_foundation_web_analysis["foundation-web-analysis"] SELF -->|runtime| n_infrastructure_fetcher["infrastructure-fetcher"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.
Targets
| Kind | Name | Source |
|---|---|---|
| lib | infrastructure_web_tech_detector | `src/lib.rs` |
Error model
No public error type was detected: no public item declares a type named *Error, and no public signature returns one.
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | yes |
| async runtime | none detected |
| database access | none detected |
| network I/O | yes |
| unsafe code | none detected |
| environment variables | none detected |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
No environment variable is read with a literal name anywhere in this crate. A variable whose key is computed at run time cannot be listed here, and is not claimed to be absent.
Related capabilities
No workspace crate depends on this one.
Verification
| Kind | Count |
|---|---|
| Unit tests | 64 |
| Integration tests | 0 |
| Examples | 0 |
| Doctests | 0 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
detector | 2 | 0 | 0 |
models | 10 | 0 | 0 |
What the tests establish, by name:
amplitude_detected—src/detector.rsanalyze_data_flags_cms_detection_low_confidence_when_no_cms_found—src/detector.rsanalyze_flags_cms_not_detected_for_manual_review—src/detector.rsbootstrap_framework_detected—src/detector.rscache_ttl_is_seven_days—src/detector.rscalendly_booking_detected—src/detector.rscontact_form_7_detected_from_wpcf7_marker—src/detector.rscrisp_widget_detected—src/detector.rsdrift_widget_detected—src/detector.rsdrupal_detected_with_medium_confidence—src/detector.rsfacebook_pixel_detected—src/detector.rsfreshchat_widget_detected—src/detector.rsgodaddy_requires_both_markers—src/detector.rsgoogle_analytics_detected_with_ga4_tracking_id—src/detector.rsgoogle_tag_manager_detected_with_container_id—src/detector.rsgravity_forms_detected_from_gform_marker—src/detector.rsheap_detected—src/detector.rshotjar_detected—src/detector.rshousecall_pro_detected—src/detector.rshubspot_chat_requires_both_script_and_conversation_marker—src/detector.rshubspot_marketing_tool_detected—src/detector.rsintercom_widget_detected—src/detector.rsjobber_detected—src/detector.rsjoomla_detected_with_medium_confidence—src/detector.rsjquery_framework_detected—src/detector.rslivechat_widget_detected—src/detector.rsmailchimp_marketing_tool_detected—src/detector.rsmissing_headers_yield_none—src/detector.rsmixpanel_detected—src/detector.rsmultiple_analytics_tools_all_detected—src/detector.rs- _… 34 more_
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 13 | 42 |
Public modules with a //! block | 2 | 2 |
pie showData
title Public items with rustdoc
"Documented" : 13
"No rustdoc detected" : 29
Metrics
| Metric | Value |
|---|---|
| Rust source files | 3 |
| Source lines | 1530 |
| Code lines | 1183 |
| Public API items | 42 |
| Public modules | 2 |
| Tests | 64 |
| Examples | 0 |
| Cargo features | 0 |
| Direct runtime dependencies | 12 |
| Workspace reverse dependencies | 0 |
pie showData
title Public API by kind
"constant" : 1
"enum" : 2
"method" : 30
"struct" : 9
pie showData
title Rust source composition
"Code" : 1183
"Blank or comment" : 347
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.