agentshield/adapter/
openclaw.rs1use std::path::{Path, PathBuf};
2
3use crate::analysis::cross_file::apply_cross_file_sanitization;
4use crate::error::Result;
5use crate::ir::taint_builder::build_data_surface;
6use crate::ir::*;
7use crate::parser;
8
9pub struct OpenClawAdapter;
13
14impl super::Adapter for OpenClawAdapter {
15 fn framework(&self) -> Framework {
16 Framework::OpenClaw
17 }
18
19 fn detect(&self, root: &Path) -> bool {
20 root.join("SKILL.md").exists()
21 }
22
23 fn load(&self, root: &Path, ignore_tests: bool) -> Result<Vec<ScanTarget>> {
24 let name = root
25 .file_name()
26 .map(|n| n.to_string_lossy().to_string())
27 .unwrap_or_else(|| "openclaw-skill".into());
28
29 let mut source_files = Vec::new();
30 let mut execution = execution_surface::ExecutionSurface::default();
31
32 let walker = ignore::WalkBuilder::new(root)
34 .hidden(true)
35 .git_ignore(true)
36 .max_depth(Some(3))
37 .build();
38
39 for entry in walker.flatten() {
40 let path = entry.path();
41 if !path.is_file() {
42 continue;
43 }
44
45 if ignore_tests && super::mcp::is_test_file(path) {
46 continue;
47 }
48
49 let ext = path
50 .extension()
51 .map(|e| e.to_string_lossy().to_string())
52 .unwrap_or_default();
53 let lang = Language::from_extension(&ext);
54
55 if !matches!(
56 lang,
57 Language::Python | Language::Shell | Language::Markdown
58 ) {
59 continue;
60 }
61
62 let metadata = std::fs::metadata(path)?;
63 if metadata.len() > 1_048_576 {
64 continue;
65 }
66
67 if let Ok(content) = std::fs::read_to_string(path) {
68 use sha2::Digest;
69 let hash = format!(
70 "{:x}",
71 sha2::Sha256::new()
72 .chain_update(content.as_bytes())
73 .finalize()
74 );
75 source_files.push(SourceFile {
76 path: path.to_path_buf(),
77 language: lang,
78 size_bytes: metadata.len(),
79 content_hash: hash,
80 content,
81 });
82 }
83 }
84
85 let mut parsed_files: Vec<(PathBuf, parser::ParsedFile)> = Vec::new();
87 for sf in &source_files {
88 if let Some(parser) = parser::parser_for_language(sf.language) {
89 if let Ok(parsed) = parser.parse_file(&sf.path, &sf.content) {
90 parsed_files.push((sf.path.clone(), parsed));
91 }
92 }
93 }
94
95 apply_cross_file_sanitization(&mut parsed_files);
97
98 for (_, parsed) in parsed_files {
100 execution.commands.extend(parsed.commands);
101 execution.file_operations.extend(parsed.file_operations);
102 execution
103 .network_operations
104 .extend(parsed.network_operations);
105 execution.env_accesses.extend(parsed.env_accesses);
106 execution.dynamic_exec.extend(parsed.dynamic_exec);
107 }
108
109 let tools = vec![];
110 let data = build_data_surface(&tools, &execution);
111
112 Ok(vec![ScanTarget {
113 name,
114 framework: Framework::OpenClaw,
115 root_path: root.to_path_buf(),
116 tools,
117 execution,
118 data,
119 dependencies: Default::default(),
120 provenance: Default::default(),
121 source_files,
122 }])
123 }
124}