keyhog-scanner 0.2.1

High-performance secret detection engine with Hyperscan NFA, GPU pattern matching, entropy scoring, and decode-through scanning
Documentation
use super::github::{base62_encode_u32, crc32};
use super::*;
use base64::Engine;

fn make_github_classic(entropy: &str) -> String {
    assert_eq!(entropy.len(), 30);
    let checksum = base62_encode_u32(crc32(entropy.as_bytes()), 6);
    format!("ghp_{entropy}{checksum}")
}

fn make_github_fine_grained(id: &str, entropy: &str) -> String {
    assert_eq!(id.len(), 22);
    assert_eq!(entropy.len(), 53);
    let payload = format!("{id}_{entropy}");
    let checksum = base62_encode_u32(crc32(payload.as_bytes()), 6);
    format!("github_pat_{payload}{checksum}")
}

fn make_npm(entropy: &str) -> String {
    assert_eq!(entropy.len(), 30);
    let checksum = base62_encode_u32(crc32(entropy.as_bytes()), 6);
    format!("npm_{entropy}{checksum}")
}

#[test]
fn crc32_known_values() {
    assert_eq!(crc32(b"123456789"), 0xCBF4_3926);
    assert_eq!(crc32(b""), 0x0000_0000);
    assert_eq!(crc32(b"test"), 0xD87F_7E0C);
}

#[test]
fn github_classic_valid() {
    let token = make_github_classic("zQWBuTSOoRi4A9spHcVY5ncnsDkxkJ");
    assert_eq!(
        GithubClassicPatValidator.validate(&token),
        ChecksumResult::Valid
    );
}

#[test]
fn github_classic_all_as_valid() {
    let token = make_github_classic(&"A".repeat(30));
    assert_eq!(
        GithubClassicPatValidator.validate(&token),
        ChecksumResult::Valid
    );
}

#[test]
fn github_classic_invalid_checksum() {
    let mut token = make_github_classic(&"B".repeat(30));
    token.pop();
    token.push('x');
    assert_eq!(
        GithubClassicPatValidator.validate(&token),
        ChecksumResult::Invalid
    );
}

#[test]
fn github_classic_not_applicable_variants() {
    assert_eq!(
        GithubClassicPatValidator.validate("gho_something"),
        ChecksumResult::NotApplicable
    );
    assert_eq!(
        GithubClassicPatValidator.validate("ghp_tooshort"),
        ChecksumResult::NotApplicable
    );
}

#[test]
fn github_fine_grained_valid() {
    let token = make_github_fine_grained(&"A".repeat(22), &"B".repeat(53));
    assert_eq!(
        GithubFineGrainedPatValidator.validate(&token),
        ChecksumResult::Valid
    );
}

#[test]
fn github_fine_grained_invalid_checksum() {
    let token = "github_pat_AAAAAAAAAAAAAAAAAAAAAA_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB000000";
    assert_eq!(
        GithubFineGrainedPatValidator.validate(token),
        ChecksumResult::Invalid
    );
}

#[test]
fn github_fine_grained_not_applicable() {
    assert_eq!(
        GithubFineGrainedPatValidator.validate("ghp_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
        ChecksumResult::NotApplicable
    );
}

#[test]
fn npm_valid_and_invalid() {
    let token = make_npm("zQWBuTSOoRi4A9spHcVY5ncnsDkxkJ");
    assert_eq!(NpmTokenValidator.validate(&token), ChecksumResult::Valid);

    let mut invalid = make_npm(&"C".repeat(30));
    invalid.pop();
    invalid.push('x');
    assert_eq!(
        NpmTokenValidator.validate(&invalid),
        ChecksumResult::Invalid
    );
    assert_eq!(
        NpmTokenValidator.validate("npm_tooshort"),
        ChecksumResult::NotApplicable
    );
}

#[test]
fn slack_valid_and_invalid_variants() {
    assert_eq!(
        SlackTokenValidator.validate("xoxb-1234567890-1234567890-abcdefghijklmnopqrstuvwx"),
        ChecksumResult::Valid
    );
    assert_eq!(
        SlackTokenValidator.validate("xoxp-1234567890-1234567890-abcdefghijklmnopqrstuvwx"),
        ChecksumResult::Valid
    );
    assert_eq!(
        SlackTokenValidator
            .validate("xoxp-1234567890-1234567890-1234567890-abcdef1234567890abcdef1234567890"),
        ChecksumResult::Valid
    );
    assert_eq!(
        SlackTokenValidator.validate("xoxb-nodashes"),
        ChecksumResult::Invalid
    );
    assert_eq!(
        SlackTokenValidator.validate("not-a-slack-token"),
        ChecksumResult::NotApplicable
    );
}

#[test]
fn pypi_valid_and_invalid_variants() {
    let blob = base64::engine::general_purpose::URL_SAFE.encode(vec![0u8; 120]);
    let token = format!("pypi-{blob}");
    assert_eq!(PypiTokenValidator.validate(&token), ChecksumResult::Valid);
    assert_eq!(
        PypiTokenValidator.validate("pypi-!!!not-valid-base64!!!"),
        ChecksumResult::Invalid
    );
    assert_eq!(
        PypiTokenValidator.validate("pypi-short"),
        ChecksumResult::Invalid
    );
    assert_eq!(
        PypiTokenValidator.validate("not-pypi-token"),
        ChecksumResult::NotApplicable
    );
}

#[test]
fn registry_routes_and_rejects() {
    let github = make_github_classic(&"D".repeat(30));
    assert_eq!(validate_checksum(&github), ChecksumResult::Valid);

    let npm = make_npm(&"E".repeat(30));
    assert_eq!(validate_checksum(&npm), ChecksumResult::Valid);

    let slack = "xoxb-1234567890-1234567890-abcdefghijklmnopqrstuvwx";
    assert_eq!(validate_checksum(slack), ChecksumResult::Valid);

    let blob = base64::engine::general_purpose::STANDARD.encode(vec![0u8; 120]);
    let pypi = format!("pypi-{blob}");
    assert_eq!(validate_checksum(&pypi), ChecksumResult::Valid);

    assert_eq!(
        validate_checksum("ghp_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA000000"),
        ChecksumResult::Invalid
    );
    assert_eq!(
        validate_checksum("npm_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA000000"),
        ChecksumResult::Invalid
    );
    assert_eq!(validate_checksum("xoxb-bad"), ChecksumResult::Invalid);
    assert_eq!(validate_checksum("pypi-!!!bad!!!"), ChecksumResult::Invalid);
    assert_eq!(
        validate_checksum("AKIAIOSFODNN7EXAMPLE"),
        ChecksumResult::NotApplicable
    );
}