1pub(crate) mod capability;
7pub mod data_surface;
8pub mod dependency_surface;
9pub mod execution_surface;
10pub mod provenance_surface;
11pub mod taint_builder;
12pub mod tool_surface;
13
14use serde::{Deserialize, Serialize};
15use std::path::PathBuf;
16
17pub use data_surface::DataSurface;
18pub use dependency_surface::DependencySurface;
19pub use execution_surface::ExecutionSurface;
20pub use provenance_surface::ProvenanceSurface;
21pub use tool_surface::{
22 Capability, CapabilityDeclaration, CapabilityDeclarationSource, CapabilityEvidence, ToolSurface,
23};
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct ScanTarget {
28 pub name: String,
30 pub framework: Framework,
32 pub root_path: PathBuf,
34 pub tools: Vec<ToolSurface>,
36 pub execution: ExecutionSurface,
38 pub data: DataSurface,
40 pub dependencies: DependencySurface,
42 pub provenance: ProvenanceSurface,
44 pub source_files: Vec<SourceFile>,
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
50#[serde(rename_all = "snake_case")]
51pub enum Framework {
52 Mcp,
53 OpenClaw,
54 HermesAgent,
55 LangChain,
56 CrewAi,
57 GptActions,
58 CursorRules,
59 VercelAi,
60 AutoGen,
61 LlamaIndex,
62 SemanticKernel,
63 Unknown,
64}
65
66impl std::fmt::Display for Framework {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 match self {
69 Self::Mcp => write!(f, "MCP"),
70 Self::OpenClaw => write!(f, "OpenClaw"),
71 Self::HermesAgent => write!(f, "Hermes Agent"),
72 Self::LangChain => write!(f, "LangChain"),
73 Self::CrewAi => write!(f, "CrewAI"),
74 Self::GptActions => write!(f, "GPT Actions"),
75 Self::CursorRules => write!(f, "Cursor Rules"),
76 Self::VercelAi => write!(f, "Vercel AI SDK"),
77 Self::AutoGen => write!(f, "AutoGen"),
78 Self::LlamaIndex => write!(f, "LlamaIndex"),
79 Self::SemanticKernel => write!(f, "Semantic Kernel"),
80 Self::Unknown => write!(f, "Unknown"),
81 }
82 }
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct SourceFile {
88 pub path: PathBuf,
89 pub language: Language,
90 pub content: String,
91 pub size_bytes: u64,
92 pub content_hash: String,
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
97#[serde(rename_all = "lowercase")]
98pub enum Language {
99 Python,
100 TypeScript,
101 JavaScript,
102 Shell,
103 Json,
104 Toml,
105 Yaml,
106 Markdown,
107 Unknown,
108}
109
110impl Language {
111 pub fn from_extension(ext: &str) -> Self {
112 match ext.to_lowercase().as_str() {
113 "py" => Self::Python,
114 "ts" | "tsx" => Self::TypeScript,
115 "js" | "jsx" | "mjs" | "cjs" => Self::JavaScript,
116 "sh" | "bash" | "zsh" => Self::Shell,
117 "json" => Self::Json,
118 "toml" => Self::Toml,
119 "yml" | "yaml" => Self::Yaml,
120 "md" | "markdown" => Self::Markdown,
121 _ => Self::Unknown,
122 }
123 }
124
125 pub fn is_documentation(&self) -> bool {
126 matches!(self, Self::Markdown)
127 }
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
132pub struct SourceLocation {
133 pub file: PathBuf,
134 pub line: usize,
135 pub column: usize,
136 pub end_line: Option<usize>,
137 pub end_column: Option<usize>,
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
145#[serde(rename_all = "snake_case")]
146pub enum ArgumentSource {
147 Literal(String),
149 Parameter { name: String },
151 EnvVar { name: String },
153 Interpolated,
155 Unknown,
157 Sanitized { sanitizer: String },
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
169pub enum SinkClass {
170 Command,
172 FilePath,
174 NetworkUrl,
176 DynamicExec,
178}
179
180impl ArgumentSource {
181 pub fn is_tainted(&self) -> bool {
188 !matches!(self, Self::Literal(_) | Self::Sanitized { .. })
189 }
190
191 pub fn is_tainted_for_sink(&self, sink: SinkClass) -> bool {
197 match self {
198 Self::Literal(_) => false,
199 Self::Sanitized { sanitizer } => {
200 !crate::analysis::cross_file::sanitizer_allows_sink(sanitizer, sink)
201 }
202 _ => true,
203 }
204 }
205}