Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,45 @@
"Bash(git grep:*)",
"Bash(find:*)",
"Bash(UUID=\"108e9186e8c5677a1b77086cce5d81d1fed81432617971b2c6993681aced1a044c89465e8c60fe20\" curl -s \"https://rekor.sigstore.dev/api/v1/log/entries/$UUID\")",
"Bash(git rm:*)"
"Bash(git rm:*)",
"Bash(awk '{print $2}')",
"Bash(git push *)",
"Bash(gh pr *)",
"Bash(git pull *)",
"Bash(git tag -a v0.8.0 -m 'Release v0.8.0 *)",
"WebFetch(domain:red.anthropic.com)",
"WebFetch(domain:blog.vidocsecurity.com)",
"Bash(cargo kani *)",
"Bash(gh search *)",
"Bash(git stash *)",
"Bash(rivet validate *)",
"Bash(git -C /Users/r/git/pulseengine/kiln status)",
"Bash(rivet list *)",
"Bash(rivet --version)",
"Bash(pip3 install *)",
"Bash(rivet show *)",
"Bash(rivet --help)",
"Bash(rivet get *)",
"Bash(rivet sync *)",
"Bash(rivet query *)",
"Bash(rivet docs *)",
"Bash(rivet coverage *)",
"Bash(rivet add *)",
"Bash(rivet link *)",
"WebFetch(domain:medium.com)",
"Bash(zola build *)",
"WebFetch(domain:ferrous-systems.com)",
"Bash(git -C /Users/r/git/pulseengine/sigil log --oneline --since=\"2025-01-23\")",
"Bash(rivet init *)",
"Bash(rivet stats *)",
"Bash(cargo check *)",
"Bash(rivet schema *)",
"Bash(cargo fmt *)",
"Bash(git branch *)",
"Bash(git reset *)",
"WebFetch(domain:arxiv.org)",
"Bash(rivet next-id *)",
"Bash(rivet validate *)"
],
"deny": [],
"ask": []
Expand Down
49 changes: 35 additions & 14 deletions src/attestation/src/dsse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ impl DsseEnvelope {
}

/// Create a DSSE envelope from a JSON-serializable payload
pub fn from_payload<T: Serialize>(payload: &T, payload_type: impl Into<String>) -> Result<Self, serde_json::Error> {
pub fn from_payload<T: Serialize>(
payload: &T,
payload_type: impl Into<String>,
) -> Result<Self, serde_json::Error> {
let json = serde_json::to_vec(payload)?;
Ok(Self::new(&json, payload_type))
}
Expand Down Expand Up @@ -134,7 +137,9 @@ impl DsseEnvelope {

/// Parse the payload as JSON
pub fn payload_json<T: for<'de> Deserialize<'de>>(&self) -> Result<T, DsseError> {
let bytes = self.payload_bytes().map_err(|e| DsseError::DecodeError(e.to_string()))?;
let bytes = self
.payload_bytes()
.map_err(|e| DsseError::DecodeError(e.to_string()))?;
serde_json::from_slice(&bytes).map_err(|e| DsseError::JsonError(e.to_string()))
}

Expand Down Expand Up @@ -163,7 +168,10 @@ impl DsseEnvelope {

/// Verify all signatures in the envelope
#[cfg(feature = "signing")]
pub fn verify_ed25519(&self, public_key: &ed25519_compact::PublicKey) -> Result<bool, DsseError> {
pub fn verify_ed25519(
&self,
public_key: &ed25519_compact::PublicKey,
) -> Result<bool, DsseError> {
use base64::Engine;

if self.signatures.is_empty() {
Expand All @@ -180,7 +188,8 @@ impl DsseEnvelope {
let signature = ed25519_compact::Signature::from_slice(&sig_bytes)
.map_err(|e| DsseError::InvalidSignature(e.to_string()))?;

public_key.verify(&pae, &signature)
public_key
.verify(&pae, &signature)
.map_err(|_| DsseError::VerificationFailed)?;
}

Expand Down Expand Up @@ -365,10 +374,7 @@ impl ResourceDescriptor {
}

/// Create for a git source
pub fn git_source(
repo_url: impl Into<String>,
commit: impl Into<String>,
) -> Self {
pub fn git_source(repo_url: impl Into<String>, commit: impl Into<String>) -> Self {
let commit = commit.into();
let mut digest = std::collections::HashMap::new();
digest.insert("gitCommit".to_string(), commit.clone());
Expand Down Expand Up @@ -515,7 +521,9 @@ mod tests {
message: String,
}

let payload = TestPayload { message: "test".to_string() };
let payload = TestPayload {
message: "test".to_string(),
};
let envelope = DsseEnvelope::from_payload(&payload, "application/json").unwrap();

assert_eq!(envelope.payload_type, "application/json");
Expand All @@ -532,7 +540,9 @@ mod tests {
tool: String,
}

let predicate = TestPredicate { tool: "test-tool".to_string() };
let predicate = TestPredicate {
tool: "test-tool".to_string(),
};
let mut statement = InTotoStatement::new(WSC_TRANSFORMATION_PREDICATE, &predicate).unwrap();

statement.add_subject("artifact.wasm", "abc123def456");
Expand All @@ -541,7 +551,10 @@ mod tests {
assert_eq!(statement.predicate_type, WSC_TRANSFORMATION_PREDICATE);
assert_eq!(statement.subject.len(), 1);
assert_eq!(statement.subject[0].name, "artifact.wasm");
assert_eq!(statement.subject[0].digest.get("sha256"), Some(&"abc123def456".to_string()));
assert_eq!(
statement.subject[0].digest.get("sha256"),
Some(&"abc123def456".to_string())
);
}

#[test]
Expand All @@ -551,7 +564,9 @@ mod tests {
version: String,
}

let predicate = TestPredicate { version: "1.0".to_string() };
let predicate = TestPredicate {
version: "1.0".to_string(),
};
let mut statement = InTotoStatement::new(WSC_TRANSFORMATION_PREDICATE, &predicate).unwrap();
statement.add_subject("test.wasm", "sha256hash");

Expand Down Expand Up @@ -583,8 +598,14 @@ mod tests {
.with_digest("sha1", "sha1hash");

assert_eq!(subject.digest.len(), 3);
assert_eq!(subject.digest.get("sha256"), Some(&"sha256hash".to_string()));
assert_eq!(subject.digest.get("sha512"), Some(&"sha512hash".to_string()));
assert_eq!(
subject.digest.get("sha256"),
Some(&"sha256hash".to_string())
);
assert_eq!(
subject.digest.get("sha512"),
Some(&"sha512hash".to_string())
);
}

#[cfg(feature = "signing")]
Expand Down
44 changes: 30 additions & 14 deletions src/attestation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,11 @@ impl TransformationAttestationBuilder {
///
/// Returns the attestation structure. The attestation_signature field
/// will have algorithm="unsigned" and empty signature.
pub fn build(self, output_bytes: &[u8], output_name: impl Into<String>) -> TransformationAttestation {
pub fn build(
self,
output_bytes: &[u8],
output_name: impl Into<String>,
) -> TransformationAttestation {
let output_hash = compute_sha256_hash(output_bytes);
let timestamp = chrono::Utc::now().to_rfc3339();
let attestation_id = generate_uuid_v4();
Expand Down Expand Up @@ -789,7 +793,7 @@ impl TransformationAttestationBuilder {

/// Compute SHA-256 hash of bytes and return as hex string
fn compute_sha256_hash(bytes: &[u8]) -> String {
use sha2::{Sha256, Digest};
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(bytes);
let result = hasher.finalize();
Expand All @@ -811,7 +815,9 @@ fn generate_uuid_v4() -> String {
u16::from_be_bytes([bytes[4], bytes[5]]),
u16::from_be_bytes([bytes[6], bytes[7]]),
u16::from_be_bytes([bytes[8], bytes[9]]),
u64::from_be_bytes([0, 0, bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]])
u64::from_be_bytes([
0, 0, bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]
])
)
}

Expand All @@ -830,11 +836,17 @@ mod tests {
.build(output, "output.wasm");

assert_eq!(attestation.version, "1.0");
assert_eq!(attestation.transformation_type, TransformationType::Optimization);
assert_eq!(
attestation.transformation_type,
TransformationType::Optimization
);
assert_eq!(attestation.tool.name, "loom");
assert_eq!(attestation.tool.version, "0.1.0");
assert_eq!(attestation.inputs.len(), 1);
assert_eq!(attestation.inputs[0].signature_status, SignatureStatus::Unsigned);
assert_eq!(
attestation.inputs[0].signature_status,
SignatureStatus::Unsigned
);
}

#[test]
Expand All @@ -855,8 +867,14 @@ mod tests {

#[test]
fn test_section_names() {
assert_eq!(TRANSFORMATION_ATTESTATION_SECTION, "wsc.transformation.attestation");
assert_eq!(TRANSFORMATION_AUDIT_TRAIL_SECTION, "wsc.transformation.audit_trail");
assert_eq!(
TRANSFORMATION_ATTESTATION_SECTION,
"wsc.transformation.attestation"
);
assert_eq!(
TRANSFORMATION_AUDIT_TRAIL_SECTION,
"wsc.transformation.audit_trail"
);
}

#[test]
Expand Down Expand Up @@ -886,7 +904,10 @@ mod tests {
assert_eq!(attestation.attestation_signature.algorithm, "ed25519");
assert!(!attestation.attestation_signature.signature.is_empty());
assert!(attestation.attestation_signature.public_key.is_some());
assert_eq!(attestation.attestation_signature.key_id, Some("test-key-id".to_string()));
assert_eq!(
attestation.attestation_signature.key_id,
Some("test-key-id".to_string())
);

// Verify it can be serialized and deserialized
let json = attestation.to_json().unwrap();
Expand All @@ -905,12 +926,7 @@ mod tests {

let result = TransformationAttestationBuilder::new_optimization("loom", "0.1.0")
.add_input_unsigned(input, "input.wasm")
.build_and_sign_ed25519(
output,
"output.wasm",
invalid_key,
None,
);
.build_and_sign_ed25519(output, "output.wasm", invalid_key, None);

assert!(result.is_err());
assert!(result.unwrap_err().contains("Invalid Ed25519 secret key"));
Expand Down
48 changes: 27 additions & 21 deletions src/attestation/src/reproducibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,10 +326,7 @@ impl BuilderIdentity {

return Some(Self {
builder_type: "github-actions".to_string(),
builder_id: format!(
"https://github.com/{}/actions/runs/{}",
repo, run_id
),
builder_id: format!("https://github.com/{}/actions/runs/{}", repo, run_id),
workflow: std::env::var("GITHUB_WORKFLOW").ok(),
job: std::env::var("GITHUB_JOB").ok(),
run_id: Some(run_id),
Expand Down Expand Up @@ -635,7 +632,11 @@ pub struct DependencyPin {

impl DependencyPin {
/// Create a new dependency pin
pub fn new(name: impl Into<String>, version: impl Into<String>, source: impl Into<String>) -> Self {
pub fn new(
name: impl Into<String>,
version: impl Into<String>,
source: impl Into<String>,
) -> Self {
Self {
name: name.into(),
version: version.into(),
Expand All @@ -650,8 +651,7 @@ impl DependencyPin {

/// Create a crates.io dependency
pub fn crates_io(name: impl Into<String>, version: impl Into<String>) -> Self {
Self::new(name, version, "crates.io")
.with_registry("https://crates.io")
Self::new(name, version, "crates.io").with_registry("https://crates.io")
}

/// Create a git dependency
Expand All @@ -668,7 +668,11 @@ impl DependencyPin {
}

/// Create a path dependency
pub fn path(name: impl Into<String>, version: impl Into<String>, path: impl Into<String>) -> Self {
pub fn path(
name: impl Into<String>,
version: impl Into<String>,
path: impl Into<String>,
) -> Self {
let mut dep = Self::new(name, version, "path");
dep.path = Some(path.into());
dep
Expand Down Expand Up @@ -745,10 +749,7 @@ pub struct ReproducibilityVerification {

impl ReproducibilityVerification {
/// Create a successful verification
pub fn success(
env: BuildEnvironment,
original_hash: impl Into<String>,
) -> Self {
pub fn success(env: BuildEnvironment, original_hash: impl Into<String>) -> Self {
let hash = original_hash.into();
Self {
is_reproducible: true,
Expand Down Expand Up @@ -802,12 +803,15 @@ mod tests {
fn test_builder_identity_github_detection() {
// Can't easily test actual detection without setting env vars
// but we can test the struct creation
let builder = BuilderIdentity::new("github-actions", "https://github.com/org/repo/actions/runs/123")
.with_workflow("CI")
.with_job("build")
.with_run_id("123")
.with_repository("org/repo")
.with_commit_sha("abc123");
let builder = BuilderIdentity::new(
"github-actions",
"https://github.com/org/repo/actions/runs/123",
)
.with_workflow("CI")
.with_job("build")
.with_run_id("123")
.with_repository("org/repo")
.with_commit_sha("abc123");

assert_eq!(builder.builder_type, "github-actions");
assert_eq!(builder.workflow, Some("CI".to_string()));
Expand All @@ -816,8 +820,7 @@ mod tests {

#[test]
fn test_dependency_pin() {
let dep = DependencyPin::crates_io("serde", "1.0.195")
.with_hash("sha256:abc123");
let dep = DependencyPin::crates_io("serde", "1.0.195").with_hash("sha256:abc123");

assert_eq!(dep.name, "serde");
assert_eq!(dep.version, "1.0.195");
Expand All @@ -835,7 +838,10 @@ mod tests {
.add_dependency(DependencyPin::crates_io("tokio", "1.0"))
.build();

assert_eq!(manifest.cargo_lock_hash, Some("sha256:lockfile123".to_string()));
assert_eq!(
manifest.cargo_lock_hash,
Some("sha256:lockfile123".to_string())
);
assert_eq!(manifest.git_commit, Some("abc123def456".to_string()));
assert_eq!(manifest.dependency_count(), 2);
}
Expand Down
Loading
Loading