agentshield/parser/python/
classify.rs1use crate::ir::ArgumentSource;
2use crate::ir::SourceLocation;
3use std::path::Path;
4
5pub(crate) fn classify_argument(
7 args_str: &str,
8 param_names: &std::collections::HashSet<String>,
9 sanitized_vars: &std::collections::HashSet<String>,
10) -> ArgumentSource {
11 let first_arg = args_str.split(',').next().unwrap_or("").trim();
12
13 if first_arg.is_empty() {
14 return ArgumentSource::Unknown;
15 }
16
17 let ident = first_arg.split('.').next().unwrap_or(first_arg);
19 let ident = ident.split('[').next().unwrap_or(ident);
20 if let Some(sanitizer) = sanitized_label_for_var(ident, sanitized_vars) {
21 return ArgumentSource::Sanitized { sanitizer };
22 }
23
24 if let Some(val) = strip_python_string_literal(first_arg) {
27 return ArgumentSource::Literal(val.to_string());
28 }
29
30 if first_arg.starts_with("f\"") || first_arg.starts_with("f'") || first_arg.contains(".format(")
32 {
33 return ArgumentSource::Interpolated;
34 }
35
36 if first_arg.contains("os.environ") || first_arg.contains("os.getenv") {
38 return ArgumentSource::EnvVar {
39 name: first_arg.to_string(),
40 };
41 }
42
43 if param_names.contains(ident) {
45 return ArgumentSource::Parameter {
46 name: ident.to_string(),
47 };
48 }
49
50 ArgumentSource::Unknown
51}
52
53pub(crate) fn strip_python_string_literal(arg: &str) -> Option<&str> {
54 arg.strip_prefix('"')
55 .and_then(|inner| inner.strip_suffix('"'))
56 .or_else(|| {
57 arg.strip_prefix('\'')
58 .and_then(|inner| inner.strip_suffix('\''))
59 })
60}
61
62pub(crate) use crate::parser::common::{sanitized_label_for_var, sanitized_var_marker};
63
64pub(crate) fn loc(file: &Path, line: usize) -> SourceLocation {
65 SourceLocation {
66 file: file.to_path_buf(),
67 line,
68 column: 0,
69 end_line: Some(line),
70 end_column: Some(0),
71 }
72}
73
74pub(crate) fn loc_from_range(
75 file: &Path,
76 line: usize,
77 source_line: &str,
78 start_byte: usize,
79 end_byte: usize,
80) -> SourceLocation {
81 SourceLocation {
82 file: file.to_path_buf(),
83 line,
84 column: source_line[..start_byte].chars().count(),
85 end_line: Some(line),
86 end_column: Some(source_line[..end_byte].chars().count()),
87 }
88}