///|
fn add_unique_source(sources : Array[Source], raw : String) -> Unit {
let normalized = normalize_token(raw)
for source in sources {
if normalize_token(source.raw) == normalized {
return
}
}
sources.push(classify_source(raw, 0))
}
///|
fn request_directive_name(kind : ResourceKind) -> String {
match kind {
Script => "script-src"
Style => "style-src"
Image => "img-src"
Font => "font-src"
Connect => "connect-src"
Frame => "frame-src"
Media => "media-src"
Worker => "worker-src"
Object => "object-src"
Manifest => "manifest-src"
Child => "child-src"
Base => "base-uri"
Form => "form-action"
Navigate => "navigate-to"
Prefetch => "prefetch-src"
Other => "default-src"
}
}
///|
fn source_for_request(req : ResourceRequest) -> String {
if req.is_inline && req.nonce.length() > 0 {
"'nonce-" + req.nonce + "'"
} else if extract_origin(req.url).length() > 0 {
extract_origin(req.url)
} else {
"'self'"
}
}
///|
fn find_bucket(buckets : Array[Directive], name : String) -> Int {
let mut i = 0
while i < buckets.length() {
if buckets[i].name == name {
return i
}
i = i + 1
}
-1
}
///|
/// Build a minimal allow-list policy from observed resource requests.
pub fn plan_from_requests(requests : Array[ResourceRequest]) -> MigrationPlan {
let directives : Array[Directive] = []
let findings : Array[Finding] = []
for req in requests {
let name = request_directive_name(req.kind)
let raw = source_for_request(req)
let index = find_bucket(directives, name)
if index < 0 {
let sources : Array[Source] = []
add_unique_source(sources, raw)
directives.push(make_directive(name, sources, "", 0))
} else {
add_unique_source(directives[index].sources, raw)
}
}
let policy = { directives, diagnostics: [] }
{ directives, findings, normalized: normalize(policy) }
}