keyhog-core 0.5.44

keyhog-core: shared data model and detector specifications for the KeyHog secret scanner
Documentation
use keyhog_core::json_selector::{select, validate, MAX_SELECTOR_BYTES, MAX_SELECTOR_SEGMENTS};

#[test]
fn resolves_shipped_object_and_array_forms() {
    let json = serde_json::json!({
        "data": {"account": {"email": "ops@example.com"}},
        "orgs": [{"name": "acme"}]
    });
    assert_eq!(
        select(&json, "$.data.account.email")
            .expect("valid selector")
            .and_then(serde_json::Value::as_str),
        Some("ops@example.com")
    );
    assert_eq!(
        select(&json, "$.orgs[0].name")
            .expect("valid selector")
            .and_then(serde_json::Value::as_str),
        Some("acme")
    );
    assert_eq!(select(&json, "$"), Ok(Some(&json)));
    let dotted_key = serde_json::json!({"a.b": true});
    assert_eq!(
        select(&dotted_key, "$[\"a.b\"]"),
        Ok(Some(&serde_json::Value::Bool(true)))
    );
    let root_array = serde_json::json!([{"naïve.key": 7}]);
    assert_eq!(
        select(&root_array, "$[0][\"naïve.key\"]")
            .expect("valid selector")
            .and_then(serde_json::Value::as_u64),
        Some(7)
    );
}

#[test]
fn distinguishes_missing_values_from_invalid_syntax() {
    let json = serde_json::json!({"items": []});
    assert_eq!(select(&json, "$.items[0].name"), Ok(None));
    for invalid in [
        "",
        ".name",
        "name",
        "$.name.",
        "$.name..value",
        "$[]",
        "/name",
    ] {
        assert!(
            validate(invalid).is_err(),
            "selector should fail: {invalid:?}"
        );
    }
}

#[test]
fn rejects_unsupported_operators_and_platform_dependent_indexes() {
    for invalid in [
        "$.items[*]",
        "$.items[?(@.live)]",
        "$..name",
        "$.white space",
        "$[00]",
        "$[1000001]",
    ] {
        assert!(
            validate(invalid).is_err(),
            "selector should fail: {invalid:?}"
        );
    }
}

#[test]
fn bounds_selector_size_depth_and_error_output() {
    let oversized = format!("$.{}", "a".repeat(MAX_SELECTOR_BYTES));
    let error = validate(&oversized).expect_err("oversized selector");
    let rendered = error.to_string();
    assert!(rendered.contains(&format!("{} bytes", oversized.len())));
    assert!(rendered.len() < 500, "error preview must stay bounded");

    let oversized_unicode = format!("$.{}", "é".repeat(MAX_SELECTOR_BYTES));
    let unicode_error = validate(&oversized_unicode).expect_err("oversized Unicode selector");
    let unicode_rendered = unicode_error.to_string();
    assert!(unicode_rendered.contains(&format!("{} bytes", oversized_unicode.len())));
    assert!(
        unicode_rendered.len() < 500,
        "Unicode error preview must stay on a bounded character boundary"
    );

    let too_deep = format!("${}", ".a".repeat(MAX_SELECTOR_SEGMENTS + 1));
    assert!(validate(&too_deep).is_err());
}