From c2ea3f3553b892971e1a3eceb4552575d2f7a03d Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Mon, 24 Aug 2026 11:53:41 +0100 Subject: [PATCH 1/2] fix(assail): recognize canonical AffineScript extension --- src/assail/analyzer.rs | 25 +++++++++++++++++++++++++ src/assemblyline.rs | 3 +++ src/types.rs | 2 +- tests/types_tests.rs | 1 + 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/assail/analyzer.rs b/src/assail/analyzer.rs index 9bc610c..dbd7fea 100644 --- a/src/assail/analyzer.rs +++ b/src/assail/analyzer.rs @@ -6561,6 +6561,31 @@ mod tests { assert_eq!(Language::detect("page.jsx"), Language::JavaScript); } + #[test] + fn analyze_affinescript_extension_reaches_dsl_analyzer() { + let tmp = TempDir::new().unwrap(); + let affine_file = tmp.path().join("ffi_boundary.affine"); + fs::write( + &affine_file, + "external one\nexternal two\nexternal three\nexternal four\n", + ) + .unwrap(); + + assert_eq!( + Language::detect(affine_file.to_str().unwrap()), + Language::AffineScript + ); + + let report = Analyzer::new(&affine_file).unwrap().analyze().unwrap(); + assert!( + report + .weak_points + .iter() + .any(|point| point.category == WeakPointCategory::UnsafeFFI), + ".affine files must reach the AffineScript analyzer, not Unknown" + ); + } + // --------------------------------------------------------------- // 2. Analyzer::new() with a valid temp directory // --------------------------------------------------------------- diff --git a/src/assemblyline.rs b/src/assemblyline.rs index 7e223b7..2b831ef 100644 --- a/src/assemblyline.rs +++ b/src/assemblyline.rs @@ -292,6 +292,8 @@ fn is_source_file(path: &Path) -> bool { | "yaml" | "yml" | "json" + | "aff" + | "affine" ) } @@ -546,6 +548,7 @@ mod tests { assert!(is_source_file(Path::new("lib.py"))); assert!(is_source_file(Path::new("app.js"))); assert!(is_source_file(Path::new("mod.gleam"))); + assert!(is_source_file(Path::new("module.affine"))); assert!(!is_source_file(Path::new("image.png"))); assert!(!is_source_file(Path::new("binary.exe"))); assert!(!is_source_file(Path::new("readme.md"))); diff --git a/src/types.rs b/src/types.rs index c99068e..4bec109 100644 --- a/src/types.rs +++ b/src/types.rs @@ -170,7 +170,7 @@ impl Language { "jtv" => Language::JuliaTheViper, "obli" => Language::Oblibeny, "anvom" => Language::Anvomidav, - "aff" => Language::AffineScript, + "aff" | "affine" => Language::AffineScript, "ephapax" | "eph" => Language::Ephapax, "bet" => Language::BetLang, "err" => Language::ErrorLang, diff --git a/tests/types_tests.rs b/tests/types_tests.rs index 217605e..6561937 100644 --- a/tests/types_tests.rs +++ b/tests/types_tests.rs @@ -90,6 +90,7 @@ fn language_detect_nextgen_dsls() { assert_eq!(Language::detect("script.woke"), Language::WokeLang); assert_eq!(Language::detect("query.vcl"), Language::VCL); assert_eq!(Language::detect("types.aff"), Language::AffineScript); + assert_eq!(Language::detect("types.affine"), Language::AffineScript); } #[test] From edd3b8ba39975083ec8ade8a39a3eb94c354ecdf Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Mon, 24 Aug 2026 12:05:31 +0100 Subject: [PATCH 2/2] fix(assail): retain AffineScript deserialization checks --- src/assail/analyzer.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/assail/analyzer.rs b/src/assail/analyzer.rs index dbd7fea..c21c436 100644 --- a/src/assail/analyzer.rs +++ b/src/assail/analyzer.rs @@ -4998,6 +4998,25 @@ impl Analyzer { }); } + // AffineScript uses ReScript-compatible JSON bindings. Preserve the + // unsafe-deserialization coverage that canonical `.affine` sources + // received when they were historically scanned as ReScript files. + if (file_path.ends_with(".aff") || file_path.ends_with(".affine")) + && content.contains("JSON.parseExn") + { + weak_points.push(WeakPoint { + file: None, + line: None, + category: WeakPointCategory::UnsafeDeserialization, + location: Some(file_path.to_string()), + severity: Severity::High, + description: format!("JSON.parseExn in {}", file_path), + recommended_attack: vec![AttackAxis::Memory], + suppressed: false, + test_context: None, + }); + } + // Resource budgets (Eclexia-specific) if file_path.ends_with(".ecl") { stats.allocation_sites += content.matches("budget").count(); @@ -6567,7 +6586,7 @@ mod tests { let affine_file = tmp.path().join("ffi_boundary.affine"); fs::write( &affine_file, - "external one\nexternal two\nexternal three\nexternal four\n", + "external one\nexternal two\nexternal three\nexternal four\nJSON.parseExn(input)\n", ) .unwrap(); @@ -6584,6 +6603,13 @@ mod tests { .any(|point| point.category == WeakPointCategory::UnsafeFFI), ".affine files must reach the AffineScript analyzer, not Unknown" ); + assert!( + report + .weak_points + .iter() + .any(|point| point.category == WeakPointCategory::UnsafeDeserialization), + ".affine files must retain JSON.parseExn detection" + ); } // ---------------------------------------------------------------