1pub mod data_surface;
7pub mod dependency_surface;
8pub mod execution_surface;
9pub mod provenance_surface;
10pub mod taint_builder;
11pub mod tool_surface;
12
13use serde::{Deserialize, Serialize};
14use std::path::PathBuf;
15
16pub use data_surface::DataSurface;
17pub use dependency_surface::DependencySurface;
18pub use execution_surface::ExecutionSurface;
19pub use provenance_surface::ProvenanceSurface;
20pub use tool_surface::ToolSurface;
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct ScanTarget {
25 pub name: String,
27 pub framework: Framework,
29 pub root_path: PathBuf,
31 pub tools: Vec<ToolSurface>,
33 pub execution: ExecutionSurface,
35 pub data: DataSurface,
37 pub dependencies: DependencySurface,
39 pub provenance: ProvenanceSurface,
41 pub source_files: Vec<SourceFile>,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
47#[serde(rename_all = "snake_case")]
48pub enum Framework {
49 Mcp,
50 OpenClaw,
51 HermesAgent,
52 LangChain,
53 CrewAi,
54 GptActions,
55 CursorRules,
56 Unknown,
57}
58
59impl std::fmt::Display for Framework {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 match self {
62 Self::Mcp => write!(f, "MCP"),
63 Self::OpenClaw => write!(f, "OpenClaw"),
64 Self::HermesAgent => write!(f, "Hermes Agent"),
65 Self::LangChain => write!(f, "LangChain"),
66 Self::CrewAi => write!(f, "CrewAI"),
67 Self::GptActions => write!(f, "GPT Actions"),
68 Self::CursorRules => write!(f, "Cursor Rules"),
69 Self::Unknown => write!(f, "Unknown"),
70 }
71 }
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct SourceFile {
77 pub path: PathBuf,
78 pub language: Language,
79 pub content: String,
80 pub size_bytes: u64,
81 pub content_hash: String,
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
86#[serde(rename_all = "lowercase")]
87pub enum Language {
88 Python,
89 TypeScript,
90 JavaScript,
91 Shell,
92 Json,
93 Toml,
94 Yaml,
95 Markdown,
96 Unknown,
97}
98
99impl Language {
100 pub fn from_extension(ext: &str) -> Self {
101 match ext.to_lowercase().as_str() {
102 "py" => Self::Python,
103 "ts" | "tsx" => Self::TypeScript,
104 "js" | "jsx" | "mjs" | "cjs" => Self::JavaScript,
105 "sh" | "bash" | "zsh" => Self::Shell,
106 "json" => Self::Json,
107 "toml" => Self::Toml,
108 "yml" | "yaml" => Self::Yaml,
109 "md" | "markdown" => Self::Markdown,
110 _ => Self::Unknown,
111 }
112 }
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
117pub struct SourceLocation {
118 pub file: PathBuf,
119 pub line: usize,
120 pub column: usize,
121 pub end_line: Option<usize>,
122 pub end_column: Option<usize>,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
130#[serde(rename_all = "snake_case")]
131pub enum ArgumentSource {
132 Literal(String),
134 Parameter { name: String },
136 EnvVar { name: String },
138 Interpolated,
140 Unknown,
142 Sanitized { sanitizer: String },
144}
145
146impl ArgumentSource {
147 pub fn is_tainted(&self) -> bool {
149 !matches!(self, Self::Literal(_) | Self::Sanitized { .. })
150 }
151}