feat(notgraph): heatmap coloring, symbol counts, per-crate module graphs, cycle callouts

This commit is contained in:
Joseph O'Brien
2026-04-02 20:53:07 -04:00
parent 5c14230935
commit b5a27108d0
28 changed files with 490 additions and 167 deletions

View File

@@ -36,7 +36,7 @@ impl Identity for EncryptedIdentity {
Err(_) => {
return Some(Err(AgeError::ParseError(
"decrypted identity file is not valid UTF-8".to_string(),
)))
)));
}
};
// Parse inner identity — only X25519 supported for now

View File

@@ -39,9 +39,9 @@ impl FileKey {
impl TryFrom<&[u8]> for FileKey {
type Error = AgeError;
fn try_from(b: &[u8]) -> Result<Self, AgeError> {
b.try_into()
.map(Self)
.map_err(|_| AgeError::ParseError(format!("file key must be 16 bytes, got {}", b.len())))
b.try_into().map(Self).map_err(|_| {
AgeError::ParseError(format!("file key must be 16 bytes, got {}", b.len()))
})
}
}

View File

@@ -63,8 +63,8 @@ pub(crate) fn derive_scrypt_key(
mod tests {
use super::*;
use crate::identities::FileKey;
use crate::recipients::scrypt::ScryptRecipient;
use crate::recipients::Recipient;
use crate::recipients::scrypt::ScryptRecipient;
#[test]
fn scrypt_wrap_unwrap_roundtrip() {
@@ -73,7 +73,9 @@ mod tests {
let identity = ScryptIdentity::new(passphrase);
let file_key = FileKey::new([0x11u8; 16]);
let stanza = recipient.wrap_file_key(&file_key).expect("wrap should succeed");
let stanza = recipient
.wrap_file_key(&file_key)
.expect("wrap should succeed");
assert_eq!(stanza.tag, "scrypt");
assert_eq!(stanza.args.len(), 2);

View File

@@ -46,7 +46,11 @@ impl Identity for SshEd25519Identity {
}
let fp_bytes = match STANDARD_NO_PAD.decode(&stanza.args[0]) {
Ok(b) => b,
Err(e) => return Some(Err(AgeError::ParseError(format!("fingerprint base64: {e}")))),
Err(e) => {
return Some(Err(AgeError::ParseError(format!(
"fingerprint base64: {e}"
))));
}
};
if fp_bytes.as_slice() != self.fingerprint() {
return None;
@@ -68,8 +72,11 @@ fn unwrap(stanza: &Stanza, identity: &SshEd25519Identity) -> Result<FileKey, Age
let shared = x25519_secret.diffie_hellman(&ephemeral_pub);
let x25519_pub = X25519PublicKey::from(&x25519_secret);
let wrap_key =
derive_wrap_key(shared.as_bytes(), ephemeral_pub.as_bytes(), x25519_pub.as_bytes())?;
let wrap_key = derive_wrap_key(
shared.as_bytes(),
ephemeral_pub.as_bytes(),
x25519_pub.as_bytes(),
)?;
let cipher = ChaCha20Poly1305::new(Key::from_slice(&wrap_key));
let nonce = Nonce::default();
@@ -104,8 +111,8 @@ pub(crate) fn derive_wrap_key(
mod tests {
use super::*;
use crate::identities::FileKey;
use crate::recipients::ssh_ed25519::SshEd25519Recipient;
use crate::recipients::Recipient;
use crate::recipients::ssh_ed25519::SshEd25519Recipient;
fn test_signing_key() -> SigningKey {
use rand::rngs::OsRng;
@@ -123,7 +130,9 @@ mod tests {
let identity = SshEd25519Identity::from_signing_key(signing_key);
let file_key = FileKey::new([0x33u8; 16]);
let stanza = recipient.wrap_file_key(&file_key).expect("wrap should succeed");
let stanza = recipient
.wrap_file_key(&file_key)
.expect("wrap should succeed");
assert_eq!(stanza.tag, "ssh-ed25519");
assert_eq!(stanza.args.len(), 2);

View File

@@ -32,7 +32,11 @@ impl Identity for SshRsaIdentity {
}
let fp = match STANDARD_NO_PAD.decode(&stanza.args[0]) {
Ok(b) => b,
Err(e) => return Some(Err(AgeError::ParseError(format!("fingerprint base64: {e}")))),
Err(e) => {
return Some(Err(AgeError::ParseError(format!(
"fingerprint base64: {e}"
))));
}
};
if fp.as_slice() != self.fingerprint() {
return None;
@@ -62,8 +66,8 @@ pub(crate) fn rsa_pubkey_fingerprint(public_key: &RsaPublicKey) -> [u8; 4] {
#[cfg(test)]
mod tests {
use super::*;
use crate::recipients::ssh_rsa::SshRsaRecipient;
use crate::recipients::Recipient;
use crate::recipients::ssh_rsa::SshRsaRecipient;
use rand::rngs::OsRng;
fn test_rsa_keypair() -> (RsaPrivateKey, RsaPublicKey) {
@@ -79,7 +83,9 @@ mod tests {
let identity = SshRsaIdentity::from_private_key(private_key);
let file_key = FileKey::new([0x55u8; 16]);
let stanza = recipient.wrap_file_key(&file_key).expect("wrap should succeed");
let stanza = recipient
.wrap_file_key(&file_key)
.expect("wrap should succeed");
assert_eq!(stanza.tag, "ssh-rsa");
assert_eq!(stanza.args.len(), 1);

View File

@@ -119,7 +119,9 @@ mod tests {
let recipient = X25519Recipient::from_public_key(public);
let identity = X25519Identity::from_static_secret(StaticSecret::from(secret_bytes));
let file_key = FileKey::new([0x42u8; 16]);
let stanza = recipient.wrap_file_key(&file_key).expect("wrap should succeed");
let stanza = recipient
.wrap_file_key(&file_key)
.expect("wrap should succeed");
assert_eq!(stanza.tag, "X25519");
assert_eq!(stanza.args.len(), 1);
let unwrapped = identity
@@ -148,7 +150,8 @@ mod tests {
let public = PublicKey::from(&secret);
let recipient_str = X25519Recipient::from_public_key(public).to_bech32();
assert!(recipient_str.starts_with("age1"));
let identity_str = X25519Identity::from_static_secret(StaticSecret::from(secret_bytes)).to_bech32();
let identity_str =
X25519Identity::from_static_secret(StaticSecret::from(secret_bytes)).to_bech32();
assert!(identity_str.starts_with("AGE-SECRET-KEY-1"));
}