id: no-format-path
language: rust
severity: error
category: security
message: "format!() to build a file path. Use Path::join() and validate the input."
note: "Path traversal via ../ if input is not validated. Validate the identifier (UUID, alphanum) before building the path."
rule:
kind: macro_invocation
all:
- has:
field: macro
regex: '^format$'
- has:
kind: token_tree
has:
kind: string_literal
regex: '(/tmp/|/var/|/etc/|\.pdf|\.json|\.csv|\.txt|\.log|\.toml|\.yaml|\.yml)\{|\{[^}]*(path|file|dir|name|id)[^}]*\}\.(pdf|json|csv|txt|log)'
ignores:
- "**/tests/**"
- "**/*_tests/**"
- "**/*_test/**"
- "**/test_*"
- "**/*_test.rs"
- "**/fixtures/**"
skip_test_code: true
tests:
should_match:
- 'let p = format!("/tmp/{}.json", user_id);'
- 'let p = format!("{file_name}.pdf");'
should_not_match:
- 'let msg = format!("hello {}", name);'
- 'let p = base_dir.join(format!("{}.json", id));'