mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-06 16:02:23 +00:00
fix: measure the guest PEM cap before trimming
The cap read pem.trim(), but edit_guest_jwt_key stores the untrimmed string, so a key with 8 KiB of leading or trailing whitespace passed the check and was stored (and reparsed per request) at full size. Measure the untrimmed input. Test covers a whitespace-padded key. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VF3v6LA9399gNphmZaHYG3
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
efc62d8e78
commit
1751fb6920
@@ -82,15 +82,16 @@ pub async fn key_source(db: &DB, w_id: &str) -> Result<Option<GuestJwtKeySource>
|
||||
pub fn decoding_key_from_pem(pem: &str) -> Result<(DecodingKey, &'static [Algorithm])> {
|
||||
use base64::{engine::general_purpose::STANDARD, Engine};
|
||||
use spki::der::Decode;
|
||||
let pem = pem.trim();
|
||||
// The key is admin-set into an unbounded `TEXT` column and reparsed on every guest-JWT
|
||||
// request; a well-formed key with an oversized modulus would pass the checks below. Refuse
|
||||
// one larger than any real public key before decoding or storing it.
|
||||
// one larger than any real public key before decoding or storing it. Measure the untrimmed
|
||||
// input: the endpoint stores what the admin sent, so whitespace padding counts too.
|
||||
if pem.len() > MAX_GUEST_PEM_LEN {
|
||||
return Err(Error::BadRequest(format!(
|
||||
"guest key is longer than {MAX_GUEST_PEM_LEN} bytes"
|
||||
)));
|
||||
}
|
||||
let pem = pem.trim();
|
||||
// A verification key must be public. jsonwebtoken 8.3 keys the public/private distinction
|
||||
// off the PEM label alone and never inspects the DER, so private material relabelled
|
||||
// `PUBLIC KEY` would be stored and then served back through the settings response. Decode
|
||||
@@ -617,6 +618,11 @@ mod tests {
|
||||
);
|
||||
let err = decoding_key_from_pem(&big).err().unwrap().to_string();
|
||||
assert!(err.contains("longer than"), "{err}");
|
||||
// Whitespace padding must count: the endpoint stores the untrimmed value, so the cap is
|
||||
// measured before trimming rather than on the small trimmed key it would otherwise see.
|
||||
let padded = format!("{}{RSA_PUBLIC}", " ".repeat(MAX_GUEST_PEM_LEN));
|
||||
let err = decoding_key_from_pem(&padded).err().unwrap().to_string();
|
||||
assert!(err.contains("longer than"), "{err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user