pyrograph 0.1.0

GPU-accelerated taint analysis for supply chain malware detection
Documentation
use std::path::{Path, PathBuf};

const BUILTINS: &[&str] = &[
    "http",
    "https",
    "fs",
    "child_process",
    "net",
    "dns",
    "vm",
    "os",
    "path",
];

/// Resolve a `require()` specifier relative to `from_file`.
///
/// - Built-in modules return `None`.
/// - Relative paths (`./foo`, `../foo`) are resolved against the parent directory
///   of `from_file` and tried with `.js`, `.json`, and `.node` extensions.
/// - External packages return `None`.
pub fn resolve_require(from_file: &Path, specifier: &str) -> Option<PathBuf> {
    if BUILTINS.contains(&specifier) {
        return None;
    }
    if specifier.starts_with('.') {
        let dir = from_file.parent().unwrap_or_else(|| Path::new("."));
        let resolved = dir.join(specifier);
        if resolved.extension().is_none() {
            for ext in ["js", "json", "node"] {
                let with_ext = resolved.with_extension(ext);
                if with_ext.exists() {
                    return with_ext.canonicalize().ok();
                }
            }
        }
        if resolved.exists() {
            return resolved.canonicalize().ok();
        }
    }
    None
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn builtin_returns_none() {
        assert_eq!(resolve_require(Path::new("/pkg/index.js"), "fs"), None);
        assert_eq!(resolve_require(Path::new("/pkg/index.js"), "http"), None);
    }

    #[test]
    fn relative_with_extension() {
        let dir = tempfile::tempdir().unwrap();
        let target = dir.path().join("helper.js");
        std::fs::File::create(&target).unwrap();
        let from = dir.path().join("index.js");
        assert_eq!(
            resolve_require(&from, "./helper.js"),
            Some(target)
        );
    }

    #[test]
    fn relative_without_extension() {
        let dir = tempfile::tempdir().unwrap();
        let target = dir.path().join("helper.js");
        std::fs::File::create(&target).unwrap();
        let from = dir.path().join("index.js");
        assert_eq!(
            resolve_require(&from, "./helper"),
            Some(target)
        );
    }

    #[test]
    fn parent_relative() {
        let dir = tempfile::tempdir().unwrap();
        let target = dir.path().join("parent.js");
        std::fs::File::create(&target).unwrap();
        let sub = dir.path().join("sub");
        std::fs::create_dir(&sub).unwrap();
        let from = sub.join("index.js");
        assert_eq!(
            resolve_require(&from, "../parent"),
            Some(target)
        );
    }

    #[test]
    fn external_returns_none() {
        assert_eq!(
            resolve_require(Path::new("/pkg/index.js"), "express"),
            None
        );
    }
}