mooncsp

    Parse, serialize, and check Content-Security-Policy headers in MoonBit

    csp
    content-security-policy
    http
    headers
    moonbit
    Download zip
    Version
    0.1.0
    License
    MIT
    Last updated
    4 hours ago
    Downloads
    1

    #MoonCSP

    MoonBit 的 Content-Security-Policy 解析、序列化与请求判定库。

    #功能

    • 解析 default-srcscript-srcstyle-srcimg-src*-src 指令
    • 源表达式:'self''none''unsafe-inline''nonce-*''sha256-*'、scheme、host、通配符
    • 规范化 to_string
    • 请求检查:default-src 回退、nonce/hash、'strict-dynamic'

    #安装与构建

    moon check --target wasm-gc --deny-warn moon test --target wasm-gc

    可运行示例:cmd/demo

    #三个示例

    #示例 1:网关转发图片

    反向代理解析响应头后,判断 CDN 图片是否允许。

    let policy = @mooncsp.parse(
    "default-src 'self'; img-src https: cdn.example.com",
    ).unwrap()
    let req = @mooncsp.request(
    "img",
    "https://cdn.example.com/a.png",
    "https://app.test",
    None,
    None,
    )
    // decide = "allow"
    let decision = @mooncsp.decide(policy, req)

    同策略下,kind=script 且 URL 为 https://evil.test/x.js 时得到 csp.blocked

    #示例 2:构建流水线校验脚本 nonce

    内联脚本必须带匹配 nonce;存在 'strict-dynamic' 时,缺少 nonce 不能靠 host 放行。

    let policy = @mooncsp.parse(
    "script-src 'self' 'strict-dynamic' 'nonce-n1'",
    ).unwrap()
    let ok = @mooncsp.request(
    "script",
    "https://evil.test/x.js",
    "https://app.test",
    Some("n1"),
    None,
    )
    // decide = "allow"
    let allowed = @mooncsp.decide(policy, ok)

    let missing = @mooncsp.request(
    "script",
    "https://app.test/app.js",
    "https://app.test",
    None,
    None,
    )
    // decide = "csp.strict-dynamic"
    let blocked = @mooncsp.decide(policy, missing)

    #示例 3:测试夹具断言样式被 'none' 拒绝

    安全测试解析 style-src 'none',确认外链 CSS 不会被允许。

    let policy = @mooncsp.parse("style-src 'none'").unwrap()
    let req = @mooncsp.request(
    "style",
    "https://app.test/a.css",
    "https://app.test",
    None,
    None,
    )
    // allows = false,decide = "csp.none"
    let allowed = policy.allows(req)
    let code = @mooncsp.decide(policy, req)

    更完整的走读见 examples/gateway.md

    #边界

    本库不是浏览器、HTML 解析器、nonce 生成器,也不实现完整的 CSP Level 3 上报管道。调用方需自行提供 origin、nonce 和已计算的 sha256。

    #许可证

    MIT

    Directive

    pub struct Directive {
    name : String
    sources : Array[Source]
    }

    One CSP directive such as script-src 'self' https:.

    Policy

    pub struct Policy {
    directives : Array[Directive]
    }

    A parsed Content-Security-Policy header.

    Policy::allows

    fn Policy::allows(self : Policy, request : Request) -> Bool

    Whether a request is allowed by this policy.

    Policy::directive

    fn Policy::directive(self : Policy, name : String) -> Directive?

    Policy::directives

    fn Policy::directives(self : Policy) -> Array[Directive]

    Directives in source order.

    Policy::names

    fn Policy::names(self : Policy) -> Array[String]

    Look up the first directive by case-insensitive name.

    Policy::to_string

    fn Policy::to_string(self : Policy) -> String

    Canonical serialization: names and keywords lower-cased, order preserved.

    Request

    pub struct Request {
    kind : String
    url : String
    origin : String
    nonce : String?
    hash_sha256 : String?
    }

    A resource request used by the violation checker.

    Source

    pub enum Source {
    None
    Self
    UnsafeInline
    UnsafeEval
    StrictDynamic
    UnsafeHashes
    ReportSample
    WasmUnsafeEval
    Host(String)
    Scheme(String)
    Nonce(String)
    Hash(String, String)
    Keyword(String)
    } derive(Eq,
    Debug
    )

    A source expression inside a CSP directive.

    decide

    fn decide(policy : Policy, request : Request) -> String

    Return allow or a stable violation code.

    explain

    fn explain(error : String) -> String

    Explain parser and checker diagnostics.

    parse

    fn parse(input : String) -> Result[Policy, String]

    Parse a CSP header into directives and source expressions.

    parse_or_error

    fn parse_or_error(input : String) -> String

    Parse or return a diagnostic explanation.

    request

    fn request(kind : String, url : String, origin : String, nonce : String?, hash_sha256 : String?) -> Request

    Build a checker request.

    Source Files