Skip to content

Commit fe17a28

Browse files
hyperpolymathclaude
andcommitted
feat(assail): exempt Julia *Ext.jl / ext/ dirs from DynamicCodeExecution
Julia's package-extension mechanism uses `eval` and `Meta.parse` as a core idiom in `*Ext.jl` files (and the conventional `ext/<Name>.jl` layout). Treating these as DCE findings produces mass false positives in any Julia repository — julia-ecosystem#6 logged 209 findings with ~202 of them this pattern. Match the shape of PR #53 (JSON-LD InsecureProtocol exemption): add a small predicate at the detection site that subtracts the known- idiomatic pattern before the WeakPoint is constructed. Regression tests cover the *Ext.jl filename, ext/ directory, and non-extension control case (which must still flag). Closes the bulk of julia-ecosystem#6. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7935204 commit fe17a28

1 file changed

Lines changed: 59 additions & 1 deletion

File tree

src/assail/analyzer.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4457,8 +4457,18 @@ impl Analyzer {
44574457
weak_points: &mut Vec<WeakPoint>,
44584458
file_path: &str,
44594459
) -> Result<()> {
4460+
// Julia package-extension pattern: *Ext.jl files use eval/Meta.parse
4461+
// idiomatically as part of the language's extension mechanism. Skip DCE
4462+
// detection for these files to avoid mass false positives.
4463+
let is_julia_package_extension = file_path.ends_with("Ext.jl")
4464+
|| file_path.starts_with("ext/")
4465+
|| file_path.contains("/ext/")
4466+
|| file_path.contains("\\ext\\");
4467+
44604468
// eval / Meta.parse (dynamic code execution)
4461-
if content.contains("eval(") || content.contains("Meta.parse(") {
4469+
if !is_julia_package_extension
4470+
&& (content.contains("eval(") || content.contains("Meta.parse("))
4471+
{
44624472
weak_points.push(WeakPoint {
44634473
file: None,
44644474
line: None,
@@ -7763,4 +7773,52 @@ pub fn safe_get_x() -> Option<String> {
77637773
"unsafe fn / unsafe extern must not count toward the unsafe-block tally"
77647774
);
77657775
}
7776+
7777+
// ---------------------------------------------------------------
7778+
// Julia package-extension DCE exemption
7779+
// ---------------------------------------------------------------
7780+
7781+
fn count_julia_dce(content: &str, file_path: &str) -> usize {
7782+
let analyzer = Analyzer::new(std::path::Path::new(".")).expect("analyzer construction");
7783+
let mut stats = ProgramStatistics::default();
7784+
let mut wp = Vec::new();
7785+
analyzer
7786+
.analyze_julia(content, &mut stats, &mut wp, file_path)
7787+
.expect("analyze_julia");
7788+
wp.iter()
7789+
.filter(|w| matches!(w.category, WeakPointCategory::DynamicCodeExecution))
7790+
.count()
7791+
}
7792+
7793+
#[test]
7794+
fn julia_ext_jl_dce_is_exempt() {
7795+
let src = r#"function __init__() Meta.parse("1 + 1") end"#;
7796+
assert_eq!(
7797+
count_julia_dce(src, "FooExt.jl"),
7798+
0,
7799+
"*Ext.jl files use eval/Meta.parse idiomatically — must be exempt"
7800+
);
7801+
}
7802+
7803+
#[test]
7804+
fn julia_ext_dir_dce_is_exempt() {
7805+
// Per Julia convention some package extensions live under ext/<Name>.jl
7806+
// rather than the trailing-Ext.jl filename — both shapes must skip DCE.
7807+
let src = r#"eval(:(x = 1))"#;
7808+
assert_eq!(
7809+
count_julia_dce(src, "ext/MyExtension.jl"),
7810+
0,
7811+
"files under ext/ must be exempt"
7812+
);
7813+
}
7814+
7815+
#[test]
7816+
fn julia_regular_file_still_flags_eval() {
7817+
// Non-extension Julia files must still report eval/Meta.parse usage.
7818+
let src = r#"function dangerous() eval(user_input) end"#;
7819+
assert!(
7820+
count_julia_dce(src, "src/dangerous.jl") > 0,
7821+
"non-extension Julia files must still flag eval()"
7822+
);
7823+
}
77667824
}

0 commit comments

Comments
 (0)