fixed github actions

This commit is contained in:
Mick Grove 2026-03-29 17:08:58 -07:00
commit fc542afa99
3 changed files with 68 additions and 12 deletions

View file

@ -7,6 +7,13 @@ All notable changes to this project will be documented in this file.
- Added live HTTP validation for Etsy, JFrog, Octopus Deploy, OpenShift, and Private AI where provider documentation supported reliable token-only checks.
- Added detection + validation rules for Anthropic Admin, Azure Speech, Azure Translator, Databento, DataStax Astra, DevCycle, Fullstory, GC Notify, and Stytch; built-in runtime rule count is now 601 with `--confidence=low`.
- Added Heroku token revocation support for both legacy UUID-format tokens and `HRKU-` platform tokens via the OAuth authorizations API.
- Added `hmac_sha256_b64key` Liquid filter for HMAC-SHA256 signing with base64-encoded keys (decodes key to raw bytes before signing), enabling correct Azure Notification Hub SAS validation.
- Integrated SLSA v3 provenance generation into the release workflow; hash computation now scopes to build artifacts only for idempotent re-runs.
- Removed Zapier webhook live validation (GET to a catch hook triggers the Zap).
- Hardened Heroku revocation regex to prevent crossing JSON object boundaries when extracting authorization IDs.
- Fixed Zendesk subdomain regex to reject trailing hyphens; renamed `ZENDESK_SUBDOMAIN` to `ZENDESK_HOST` for clarity.
- Fixed Stytch and Polymarket trailing `\b` boundaries that prevented matching base64-padded secrets ending with `=`.
- Tightened Kubernetes API Server URL pattern to require kube-specific identifiers, preventing bootstrap tokens from binding to unrelated `server:` entries.
## [v1.91.0]
- Added SSRF protection for credential validation: outbound HTTP requests now block connections to loopback, private, link-local, and other non-public IP addresses. HTTP redirect targets are DNS-resolved and validated against the same SSRF rules. Use `--allow-internal-ips` to opt out when scanning internal infrastructure.

View file

@ -91,20 +91,15 @@ rules:
(?:notification\s*hub|Endpoint\s*=\s*sb://[a-z0-9-]{2,63}\.servicebus\.windows\.net/?)
(?:.|[\n\r]){0,160}?
SharedAccessKey
\s*[:=]\s*
["']?
(
[A-Za-z0-9+/]{32,88}={0,2}
)
|
\b
(?:hubAccessKey|notificationHub(?:Access)?Key)
\b
\s*[:=]\s*
["']?
(
[A-Za-z0-9+/]{32,88}={0,2}
)
)
\s*[:=]\s*
["']?
(
[A-Za-z0-9+/]{32,88}={0,2}
)
["']?
(?:[^A-Za-z0-9+/=]|$)
@ -149,8 +144,7 @@ rules:
{%- assign se = "" | unix_timestamp | plus: 300 -%}
{%- capture to_sign -%}{{ uri | url_encode }}
{{ se }}{%- endcapture -%}
{%- assign key_bytes = TOKEN | b64dec -%}
{%- capture auth -%}SharedAccessSignature sr={{ uri | url_encode }}&sig={{ to_sign | hmac_sha256: key_bytes | url_encode }}&se={{ se }}&skn={{ NH_KEY_NAME | url_encode }}{%- endcapture -%}
{%- capture auth -%}SharedAccessSignature sr={{ uri | url_encode }}&sig={{ to_sign | hmac_sha256_b64key: TOKEN | url_encode }}&se={{ se }}&skn={{ NH_KEY_NAME | url_encode }}{%- endcapture -%}
{{ auth | strip_newlines }}
response_matcher:
- report_response: true

View file

@ -182,6 +182,45 @@ impl Filter for HmacSha256Filter {
}
}
// ── HMAC-SHA256 with base64-encoded key ──────────────────────────────────
#[derive(Debug, FilterParameters)]
struct HmacB64KeyArgs {
#[parameter(description = "Base64-encoded HMAC key", arg_type = "str")]
key: Expression,
}
#[derive(Clone, ParseFilter, FilterReflection, Default)]
#[filter(
name = "hmac_sha256_b64key",
description = "HMAC-SHA256 with a base64-encoded key decodes the key to raw bytes before signing. Returns Base64.",
parameters(HmacB64KeyArgs),
parsed(HmacSha256B64KeyFilter)
)]
pub struct HmacSha256B64Key;
#[derive(Debug, FromFilterParameters, Display_filter)]
#[name = "hmac_sha256_b64key"]
struct HmacSha256B64KeyFilter {
#[parameters]
args: HmacB64KeyArgs,
}
impl Filter for HmacSha256B64KeyFilter {
fn evaluate(&self, input: &dyn ValueView, runtime: &dyn Runtime) -> Result<Value> {
let args = self.args.evaluate(runtime)?;
let key_b64 = args.key.to_kstr();
let key_bytes = general_purpose::STANDARD.decode(key_b64.as_bytes()).map_err(|e| {
LiquidError::with_msg(format!("hmac_sha256_b64key: invalid base64 key: {e}"))
})?;
let mut mac = Hmac::<Sha256>::new_from_slice(&key_bytes)
.map_err(|e| LiquidError::with_msg(format!("hmac_sha256_b64key: {e}")))?;
mac.update(input.to_kstr().as_bytes());
Ok(Value::scalar(general_purpose::STANDARD.encode(mac.finalize().into_bytes())))
}
}
// ── HMAC-SHA1 ─────────────────────────────────────────────
#[derive(Debug, FilterParameters)]
struct HmacSha1Args {
@ -923,6 +962,7 @@ pub fn register_all(builder: liquid::ParserBuilder) -> liquid::ParserBuilder {
.filter(Base62Filter::default())
.filter(Base36Filter::default())
.filter(HmacSha256::default())
.filter(HmacSha256B64Key::default())
.filter(HmacSha1::default())
.filter(HmacSha384::default())
}
@ -1073,6 +1113,21 @@ mod tests {
assert_eq!(render(r#"{{ "hi!" | hmac_sha256: "secret" }}"#), expect);
}
#[test]
fn hmac_sha256_b64key_filter() {
// Key is base64-encoded; the filter must decode it to raw bytes before HMAC.
let raw_key: &[u8] = &[0x00, 0x80, 0xFF, 0x42, 0xDE, 0xAD, 0xBE, 0xEF];
let b64_key = general_purpose::STANDARD.encode(raw_key);
let data = b"hello azure";
let mut mac = Hmac::<Sha256>::new_from_slice(raw_key).unwrap();
mac.update(data);
let expect = general_purpose::STANDARD.encode(mac.finalize().into_bytes());
let template = format!(r#"{{{{ "hello azure" | hmac_sha256_b64key: "{b64_key}" }}}}"#);
assert_eq!(render(&template), expect);
}
#[test]
fn hmac_sha384_filter() {
let key = b"topsecret";