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 LangChain,
52 CrewAi,
53 GptActions,
54 CursorRules,
55 Unknown,
56}
57
58impl std::fmt::Display for Framework {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 match self {
61 Self::Mcp => write!(f, "MCP"),
62 Self::OpenClaw => write!(f, "OpenClaw"),
63 Self::LangChain => write!(f, "LangChain"),
64 Self::CrewAi => write!(f, "CrewAI"),
65 Self::GptActions => write!(f, "GPT Actions"),
66 Self::CursorRules => write!(f, "Cursor Rules"),
67 Self::Unknown => write!(f, "Unknown"),
68 }
69 }
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct SourceFile {
75 pub path: PathBuf,
76 pub language: Language,
77 pub content: String,
78 pub size_bytes: u64,
79 pub content_hash: String,
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
84#[serde(rename_all = "lowercase")]
85pub enum Language {
86 Python,
87 TypeScript,
88 JavaScript,
89 Shell,
90 Json,
91 Toml,
92 Yaml,
93 Markdown,
94 Unknown,
95}
96
97impl Language {
98 pub fn from_extension(ext: &str) -> Self {
99 match ext.to_lowercase().as_str() {
100 "py" => Self::Python,
101 "ts" | "tsx" => Self::TypeScript,
102 "js" | "jsx" | "mjs" | "cjs" => Self::JavaScript,
103 "sh" | "bash" | "zsh" => Self::Shell,
104 "json" => Self::Json,
105 "toml" => Self::Toml,
106 "yml" | "yaml" => Self::Yaml,
107 "md" | "markdown" => Self::Markdown,
108 _ => Self::Unknown,
109 }
110 }
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
115pub struct SourceLocation {
116 pub file: PathBuf,
117 pub line: usize,
118 pub column: usize,
119 pub end_line: Option<usize>,
120 pub end_column: Option<usize>,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
128#[serde(rename_all = "snake_case")]
129pub enum ArgumentSource {
130 Literal(String),
132 Parameter { name: String },
134 EnvVar { name: String },
136 Interpolated,
138 Unknown,
140 Sanitized { sanitizer: String },
142}
143
144impl ArgumentSource {
145 pub fn is_tainted(&self) -> bool {
147 !matches!(self, Self::Literal(_) | Self::Sanitized { .. })
148 }
149}