0de4c82a16
req: examples/005
71 lines
2.5 KiB
Rust
71 lines
2.5 KiB
Rust
use std::path::{Path, PathBuf};
|
|
|
|
#[test]
|
|
fn canonical_examples_do_not_author_browser_javascript() {
|
|
// req: examples/005
|
|
let root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
|
|
let examples = root.join("examples");
|
|
let mut failures = Vec::new();
|
|
scan_examples(&examples, &mut |path, text| {
|
|
if path.components().any(|part| part.as_os_str() == "tests") {
|
|
return;
|
|
}
|
|
for (line_no, line) in text.lines().enumerate() {
|
|
let trimmed = line.trim();
|
|
if trimmed.contains("<script") && !trimmed.contains(r#"<script src="/slhx.js" defer></script>"#) {
|
|
failures.push(format!("{}:{}: inline <script> is not allowed", path.display(), line_no + 1));
|
|
}
|
|
if contains_inline_event_handler(trimmed) {
|
|
failures.push(format!("{}:{}: inline on*= handler is not allowed", path.display(), line_no + 1));
|
|
}
|
|
if trimmed.to_ascii_lowercase().contains("javascript:") {
|
|
failures.push(format!("{}:{}: javascript: URL is not allowed", path.display(), line_no + 1));
|
|
}
|
|
}
|
|
});
|
|
|
|
assert!(failures.is_empty(), "{}", failures.join("\n"));
|
|
}
|
|
|
|
fn scan_examples(dir: &Path, visit: &mut impl FnMut(&Path, &str)) {
|
|
for entry in std::fs::read_dir(dir).unwrap() {
|
|
let entry = entry.unwrap();
|
|
let path = entry.path();
|
|
if path.is_dir() {
|
|
scan_examples(&path, visit);
|
|
continue;
|
|
}
|
|
if !is_example_source(&path) {
|
|
continue;
|
|
}
|
|
let text = std::fs::read_to_string(&path).unwrap();
|
|
visit(&path, &text);
|
|
}
|
|
}
|
|
|
|
fn is_example_source(path: &PathBuf) -> bool {
|
|
matches!(path.extension().and_then(|ext| ext.to_str()), Some("rs" | "heml" | "html"))
|
|
}
|
|
|
|
fn contains_inline_event_handler(line: &str) -> bool {
|
|
let bytes = line.as_bytes();
|
|
let mut i = 0;
|
|
while i + 3 < bytes.len() {
|
|
let boundary = bytes[i].is_ascii_whitespace() || bytes[i] == b'<';
|
|
if boundary && bytes[i + 1] == b'o' && bytes[i + 2] == b'n' && bytes[i + 3].is_ascii_lowercase() {
|
|
let mut j = i + 4;
|
|
while j < bytes.len() && bytes[j].is_ascii_lowercase() {
|
|
j += 1;
|
|
}
|
|
while j < bytes.len() && bytes[j].is_ascii_whitespace() {
|
|
j += 1;
|
|
}
|
|
if j < bytes.len() && bytes[j] == b'=' {
|
|
return true;
|
|
}
|
|
}
|
|
i += 1;
|
|
}
|
|
false
|
|
}
|