mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-10 10:01:43 -10:00
fix(tui): compare the pet owner token in constant time (§99)
The companion server's Bearer and cookie checks used plain byte equality. Swap both for a length-folded XOR compare with no early exit. The rest of the surface already holds: loopback-only bind, Host and Origin pinning, 0700 owner-checked directory, HttpOnly SameSite=Strict cookie, CSP headers, presentation-only routes.
This commit is contained in:
1 parent
56ee8cd4e2
commit
fdff4514a9
1 file changed
+15
-2
@@ -171,6 +171,16 @@ fn valid_source(id: &str) -> bool {
|
||||
.all(|b| b.is_ascii_alphanumeric() || b"-_:./".contains(&b))
|
||||
}
|
||||
|
||||
fn token_matches(candidate: &[u8], expected: &[u8]) -> bool {
|
||||
let mut diff = candidate.len() ^ expected.len();
|
||||
for i in 0..candidate.len().max(expected.len()) {
|
||||
diff |= usize::from(
|
||||
candidate.get(i).copied().unwrap_or(0) ^ expected.get(i).copied().unwrap_or(0),
|
||||
);
|
||||
}
|
||||
diff == 0
|
||||
}
|
||||
|
||||
fn authorized(headers: &HeaderMap, state: &Service) -> bool {
|
||||
let origin = format!("http://127.0.0.1:{}", state.descriptor.port);
|
||||
let host = format!("127.0.0.1:{}", state.descriptor.port);
|
||||
@@ -187,11 +197,14 @@ fn authorized(headers: &HeaderMap, state: &Service) -> bool {
|
||||
let cookie = format!("cw_pet={}", state.descriptor.token);
|
||||
headers
|
||||
.get("authorization")
|
||||
.is_some_and(|v| v.as_bytes() == bearer.as_bytes())
|
||||
.is_some_and(|v| token_matches(v.as_bytes(), bearer.as_bytes()))
|
||||
|| headers
|
||||
.get("cookie")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.is_some_and(|v| v.split(';').any(|v| v.trim() == cookie))
|
||||
.is_some_and(|v| {
|
||||
v.split(';')
|
||||
.any(|v| token_matches(v.trim().as_bytes(), cookie.as_bytes()))
|
||||
})
|
||||
}
|
||||
|
||||
fn answer(result: Result<Value, String>) -> Response {
|
||||
|
||||
Reference in new issue
Block a user