moonsqlguard

    MoonBit port of the libinjection SQLi core

    sqli
    libinjection
    sql-injection
    tokenizer
    fingerprint
    Download zip
    Version
    0.1.0
    License
    BSD-3-Clause
    Last updated
    5 hours ago
    Downloads
    1

    #MoonSQLGuard

    libinjection 的 SQLi 检测核心移植到 MoonBit。Web 服务或离线审计拿到的是原始字段字节,这里返回是否命中、指纹和可打印的 token 文本。

    固定上游:d88a8f86d617ac8dcb9169c9f34637aaac71ac76。许可证 BSD-3-Clause。XSS/HTML5 分支不在本库范围内。检测结果不能代替参数化查询。

    #安装

    moon add hutingyu-nuist/moonsqlguard

    let d = @moonsqlguard.inspect(b"1 OR 1=1")
    println(@moonsqlguard.format_detection(d))

    #API

    • tokenize / fingerprint:按方言和引号上下文做词法和折叠
    • inspect / inspect_context / is_sqli / sqli_fingerprint:走上游的无引号、单引号、双引号和 MySQL 再解析
    • scan_fields / format_detection:按字段扫描,输出 sqli <指纹>clean <指纹>
    • dump_tokens / dump_folded / format_token:testdriver 同款文本,方便和 C 夹具对拍

    #场景

    登录接口把三个表单字段交给检测器。用户名是 admin'--,密码是普通字符串 hunter2,搜索框是 1 OR 1=1

    moon run examples/web_field --target wasm-gc

    username sqli sc password clean n q sqli 1&1

    审计脚本扫一行已经拆好的日志列:user=aliceq=1 OR 1=1ua=Mozilla/5.0id=1--。只要命中的字段,顺序和输入一致。

    moon run examples/log_scan --target wasm-gc

    hits 2 q sqli 1&1 id sqli 1c

    对照 pinned testdriver 时,直接把折叠 token 打出来:1 OR 1=1 指纹是 1&1UNION SELECT 1UE1hello 指纹为空。这是和 C 程序对文本,不是口头兼容。

    moon run examples/fixture_dump --target wasm-gc

    #验证

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

    本地对 pinned testdriver:test-sqli-* 50/50,test-folding-* 118/118,收录的 test-tokens-* 204 条期望字符串一致。当前 27 个 MoonBit 测试块走 wasm-gc;wasm / js / native 做 check --deny-warn

    #边界

    • 不做 URL 解码、HTML 实体解码
    • 不含 XSS/HTML5 分支,也不是 WAF 或 ORM
    • token 值最多 31 字节,和上游 stoken_t 一样
    • 兼容性以夹具为准,不宣称 100% 复刻每一处 C 细节

    #许可证

    BSD-3-Clause。版权与上游说明见 LICENSETHIRD_PARTY.md

    Detection

    pub(all) struct Detection {
    hit : Bool
    pattern : String
    quote : Quote
    dialect : Dialect
    tokens : Array[Token]
    } derive(Eq,
    Debug
    )

    One parsing context after folding, blacklist lookup and whitelist filtering.

    Dialect

    pub(all) enum Dialect {
    Ansi
    Mysql
    } derive(Eq,
    Debug
    )

    Field

    pub(all) struct Field {
    name : String
    value : Bytes
    } derive(Eq,
    Debug
    )

    A named request field or log column presented to the detector.

    FieldHit

    pub(all) struct FieldHit {
    name : String
    detection : Detection
    } derive(Eq,
    Debug
    )

    A field whose bytes were classified as SQLi.

    Fingerprint

    pub(all) struct Fingerprint {
    pattern : String
    tokens : Array[Token]
    consumed_tokens : Int
    ambiguous_comments : Int
    hashes : Int
    } derive(Eq,
    Debug
    )

    Folded tokens and lexer statistics for one explicit parsing context.

    Quote

    pub(all) enum Quote {
    None
    Single
    Double
    } derive(Eq,
    Debug
    )

    Token

    pub(all) struct Token {
    kind : String
    pos : Int
    value : Bytes
    open : Int
    close : Int
    count : Int
    } derive(Eq,
    Debug
    )

    Values retain at most 31 bytes, matching upstream stoken_t.

    dump_folded

    fn dump_folded(data : Bytes, dialect? : Dialect, quote? : Quote) -> String

    Dump fold_engine tokens. This is not fingerprint(): no empty-backtick rewrite.

    dump_tokens

    fn dump_tokens(data : Bytes, dialect? : Dialect, quote? : Quote) -> String

    Tokenize and dump in testdriver order. Trailing whitespace is trimmed.

    fingerprint

    fn fingerprint(data : Bytes, dialect? : Dialect, quote? : Quote) -> Fingerprint

    Run the upstream bounded folding algorithm in one quote/dialect context.

    format_detection

    fn format_detection(d : Detection) -> String

    Format a verdict for logs: sqli <pattern> or clean <pattern>.

    format_token

    fn format_token(t : Token) -> String

    One testdriver line: kind value, with quote marks restored for strings.

    inspect

    fn inspect(data : Bytes) -> Detection

    Walk the upstream no-quote / single-quote / double-quote contexts.

    inspect_context

    fn inspect_context(data : Bytes, dialect? : Dialect, quote? : Quote) -> Detection

    Fold and classify input in one explicit quote/dialect context.

    is_sqli

    fn is_sqli(data : Bytes) -> Bool

    True when any upstream detection context classifies the bytes as SQLi.

    scan_fields

    fn scan_fields(fields : Array[Field]) -> Array[FieldHit]

    Inspect each field in order and keep only SQLi hits.

    sqli_fingerprint

    fn sqli_fingerprint(data : Bytes) -> String

    C libinjection_sqli fingerprint: folded pattern on hit, otherwise empty.

    tokenize

    fn tokenize(data : Bytes, dialect? : Dialect, quote? : Quote) -> Array[Token]

    Tokenize bytes without URL decoding or text normalization.