style: apply cargo fmt to ws-origin-check changes
This commit is contained in:
@ -169,9 +169,7 @@ pub fn derive_allowed_origins(sso_callback_url: &str) -> Vec<String> {
|
|||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
// Authority is everything up to the first `/`, `?`, or `#`.
|
// Authority is everything up to the first `/`, `?`, or `#`.
|
||||||
let authority_end = rest
|
let authority_end = rest.find(['/', '?', '#']).unwrap_or(rest.len());
|
||||||
.find(['/', '?', '#'])
|
|
||||||
.unwrap_or(rest.len());
|
|
||||||
let authority = &rest[..authority_end];
|
let authority = &rest[..authority_end];
|
||||||
if authority.is_empty() {
|
if authority.is_empty() {
|
||||||
return vec![];
|
return vec![];
|
||||||
|
|||||||
@ -101,9 +101,7 @@ fn parse_origin_header(value: &str) -> Option<Origin> {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// Authority is everything up to the first `/`, `?`, or `#`.
|
// Authority is everything up to the first `/`, `?`, or `#`.
|
||||||
let authority_end = rest
|
let authority_end = rest.find(['/', '?', '#']).unwrap_or(rest.len());
|
||||||
.find(['/', '?', '#'])
|
|
||||||
.unwrap_or(rest.len());
|
|
||||||
let authority = &rest[..authority_end];
|
let authority = &rest[..authority_end];
|
||||||
if authority.is_empty() {
|
if authority.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
@ -145,12 +143,12 @@ fn is_origin_allowed(origin: &Origin, allowlist: &[String]) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let incoming = origin.canonical();
|
let incoming = origin.canonical();
|
||||||
allowlist.iter().any(|entry| {
|
allowlist
|
||||||
match parse_origin_header(entry) {
|
.iter()
|
||||||
|
.any(|entry| match parse_origin_header(entry) {
|
||||||
Some(parsed) => parsed.canonical() == incoming,
|
Some(parsed) => parsed.canonical() == incoming,
|
||||||
None => false,
|
None => false,
|
||||||
}
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the `Origin` header from a request and check it against the
|
/// Read the `Origin` header from a request and check it against the
|
||||||
@ -172,7 +170,7 @@ fn check_origin(
|
|||||||
),
|
),
|
||||||
"missing",
|
"missing",
|
||||||
));
|
));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
let raw_str = match raw.to_str() {
|
let raw_str = match raw.to_str() {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
@ -185,7 +183,7 @@ fn check_origin(
|
|||||||
),
|
),
|
||||||
"non-ascii",
|
"non-ascii",
|
||||||
));
|
));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
let origin = match parse_origin_header(raw_str) {
|
let origin = match parse_origin_header(raw_str) {
|
||||||
Some(o) => o,
|
Some(o) => o,
|
||||||
@ -198,7 +196,7 @@ fn check_origin(
|
|||||||
),
|
),
|
||||||
"malformed",
|
"malformed",
|
||||||
));
|
));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
if !is_origin_allowed(&origin, allowlist) {
|
if !is_origin_allowed(&origin, allowlist) {
|
||||||
return Err((
|
return Err((
|
||||||
@ -425,7 +423,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parse_lowercases_scheme() {
|
fn parse_lowercases_scheme() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_origin_header("HTTPS://App.Example.com").unwrap().scheme,
|
parse_origin_header("HTTPS://App.Example.com")
|
||||||
|
.unwrap()
|
||||||
|
.scheme,
|
||||||
"https"
|
"https"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -546,7 +546,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn allowed_default_port_normalization_allowlist() {
|
fn allowed_default_port_normalization_allowlist() {
|
||||||
let o = parse_origin_header("https://app.example.com").unwrap();
|
let o = parse_origin_header("https://app.example.com").unwrap();
|
||||||
assert!(is_origin_allowed(&o, &["https://app.example.com:443".into()]));
|
assert!(is_origin_allowed(
|
||||||
|
&o,
|
||||||
|
&["https://app.example.com:443".into()]
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -619,7 +622,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_rejects_disallowed_origin() {
|
fn check_rejects_disallowed_origin() {
|
||||||
let mut h = HeaderMap::new();
|
let mut h = HeaderMap::new();
|
||||||
h.insert(axum::http::header::ORIGIN, "https://evil.example".parse().unwrap());
|
h.insert(
|
||||||
|
axum::http::header::ORIGIN,
|
||||||
|
"https://evil.example".parse().unwrap(),
|
||||||
|
);
|
||||||
let err = check_origin(&h, &["https://app.example.com".into()]).unwrap_err();
|
let err = check_origin(&h, &["https://app.example.com".into()]).unwrap_err();
|
||||||
assert_eq!(err.0 .0, StatusCode::FORBIDDEN);
|
assert_eq!(err.0 .0, StatusCode::FORBIDDEN);
|
||||||
assert_eq!(err.1, "not-allowlisted");
|
assert_eq!(err.1, "not-allowlisted");
|
||||||
@ -628,7 +634,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_rejects_empty_allowlist() {
|
fn check_rejects_empty_allowlist() {
|
||||||
let mut h = HeaderMap::new();
|
let mut h = HeaderMap::new();
|
||||||
h.insert(axum::http::header::ORIGIN, "https://app.example.com".parse().unwrap());
|
h.insert(
|
||||||
|
axum::http::header::ORIGIN,
|
||||||
|
"https://app.example.com".parse().unwrap(),
|
||||||
|
);
|
||||||
let err = check_origin(&h, &[]).unwrap_err();
|
let err = check_origin(&h, &[]).unwrap_err();
|
||||||
assert_eq!(err.0 .0, StatusCode::FORBIDDEN);
|
assert_eq!(err.0 .0, StatusCode::FORBIDDEN);
|
||||||
assert_eq!(err.1, "not-allowlisted");
|
assert_eq!(err.1, "not-allowlisted");
|
||||||
@ -637,7 +646,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_allows_valid_origin() {
|
fn check_allows_valid_origin() {
|
||||||
let mut h = HeaderMap::new();
|
let mut h = HeaderMap::new();
|
||||||
h.insert(axum::http::header::ORIGIN, "https://app.example.com".parse().unwrap());
|
h.insert(
|
||||||
|
axum::http::header::ORIGIN,
|
||||||
|
"https://app.example.com".parse().unwrap(),
|
||||||
|
);
|
||||||
assert!(check_origin(&h, &["https://app.example.com".into()]).is_ok());
|
assert!(check_origin(&h, &["https://app.example.com".into()]).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -654,7 +666,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_allows_case_insensitive_host() {
|
fn check_allows_case_insensitive_host() {
|
||||||
let mut h = HeaderMap::new();
|
let mut h = HeaderMap::new();
|
||||||
h.insert(axum::http::header::ORIGIN, "https://App.Example.com".parse().unwrap());
|
h.insert(
|
||||||
|
axum::http::header::ORIGIN,
|
||||||
|
"https://App.Example.com".parse().unwrap(),
|
||||||
|
);
|
||||||
assert!(check_origin(&h, &["https://app.example.com".into()]).is_ok());
|
assert!(check_origin(&h, &["https://app.example.com".into()]).is_ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,21 @@
|
|||||||
**Rule:** Check the obvious source (gitea repo, Vaultwarden store) before spinning wheels on complex alternatives.
|
**Rule:** Check the obvious source (gitea repo, Vaultwarden store) before spinning wheels on complex alternatives.
|
||||||
**Status:** Active
|
**Status:** Active
|
||||||
|
|
||||||
|
## 2026-06-02: SSH_ASKPASS=/dev/null Blocks Git Commit Signing
|
||||||
|
**Pattern:** The container environment sets `SSH_ASKPASS=/dev/null` and `SSH_ASKPASS_REQUIRE=force`, which overrides ssh-agent and prevents git from finding signing keys during commit signing.
|
||||||
|
**Mistake:** Attempted git commit multiple times without checking why it hung. The signing key was in ssh-agent but SSH_ASKPASS was redirecting the passphrase prompt to /dev/null (not executable), causing the commit to fail with "incorrect passphrase".
|
||||||
|
**Fix:** Unset `SSH_ASKPASS` and `SSH_ASKPASS_REQUIRE` before running git commit, then use `ssh-add` with the passphrase from Vaultwarden to add the signing key to ssh-agent.
|
||||||
|
**Rule:** Before git commit signing, check `echo $SSH_ASKPASS` and `echo $SSH_ASKPASS_REQUIRE`. If SSH_ASKPASS is set to /dev/null or another non-executable, unset both variables before committing.
|
||||||
|
**Rule:** Always retrieve signing key passphrases from Vaultwarden using `vw_client.py get`, not from local files or memory.
|
||||||
|
**Status:** Active
|
||||||
|
|
||||||
|
## 2026-06-02: Always Run credential-bootstrap at Session Start
|
||||||
|
**Pattern:** Profile rules mandate running `bash /a0/usr/skills/credential-bootstrap/scripts/bootstrap.sh` at the start of every conversation before any SSH or authenticated operations. I violated this rule by starting work without bootstrapping.
|
||||||
|
**Mistake:** Began implementation work without running credential-bootstrap, then wasted multiple attempts trying to commit with a signing key that wasn't in ssh-agent.
|
||||||
|
**Rule:** ALWAYS run credential-bootstrap at session start, before any authenticated operations. This includes git commit signing.
|
||||||
|
**Rule:** If a credential operation fails, STOP and run credential-bootstrap before retrying. Do not attempt workarounds.
|
||||||
|
**Status:** Active
|
||||||
|
|
||||||
## 2026-05-08: Vaultwarden Is the Source of Truth for All Credentials
|
## 2026-05-08: Vaultwarden Is the Source of Truth for All Credentials
|
||||||
**Pattern:** SSH keys in ~/.ssh/ are ephemeral — lost on every container recreation. Local copies are unreliable.
|
**Pattern:** SSH keys in ~/.ssh/ are ephemeral — lost on every container recreation. Local copies are unreliable.
|
||||||
**Rule:** ALWAYS pull credentials (SSH keys, API tokens, passwords) from Vaultwarden when needed. Do NOT rely on local copies in ~/.ssh/ or /a0/usr/storage/ as they may be stale or missing after container recreation.
|
**Rule:** ALWAYS pull credentials (SSH keys, API tokens, passwords) from Vaultwarden when needed. Do NOT rely on local copies in ~/.ssh/ or /a0/usr/storage/ as they may be stale or missing after container recreation.
|
||||||
|
|||||||
Reference in New Issue
Block a user