Skip to content

Commit 1811b7b

Browse files
feat(assail): exempt Julia *Ext.jl from DynamicCodeExecution (idiomatic package-extension pattern) (#71)
## Summary Adds a small predicate at the `analyze_julia` DCE detection site that subtracts the Julia package-extension idiom: `*Ext.jl` files and the conventional `ext/<Name>.jl` directory layout. These files use `eval` / `Meta.parse` legitimately as part of the language's extension mechanism. Mirrors the shape of #53 (JSON-LD InsecureProtocol exemption) — small guard inline at the detector, plus regression tests for the exempt and non-exempt cases. ## Motivation `julia-ecosystem#6` logged 209 panic-attack findings of which ~202 were this exact pattern. Without an exemption, every Julia repo with a single package extension produces a flood of false positives that drowns out real findings. ## Changes - `src/assail/analyzer.rs` (analyze_julia): guard adds `is_julia_package_extension` check matching `*Ext.jl` / `ext/` / `/ext/` paths before pushing the `DynamicCodeExecution` WeakPoint. - New tests in `#[cfg(test)] mod tests`: - `julia_ext_jl_dce_is_exempt` — \`FooExt.jl\` with \`Meta.parse\` is exempt - `julia_ext_dir_dce_is_exempt` — \`ext/MyExtension.jl\` with \`eval\` is exempt - `julia_regular_file_still_flags_eval` — non-extension file still flags ## Test plan - [x] \`cargo test --lib julia_\` — all 3 new tests pass locally - [ ] CI green - [ ] Re-scan julia-ecosystem to confirm ~202 findings drop 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7935204 commit 1811b7b

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)