socket_patch_core/manifest/
schema.rs1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
6pub struct VulnerabilityInfo {
7 pub cves: Vec<String>,
8 pub summary: String,
9 pub severity: String,
10 pub description: String,
11}
12
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
15#[serde(rename_all = "camelCase")]
16pub struct PatchFileInfo {
17 pub before_hash: String,
18 pub after_hash: String,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
23#[serde(rename_all = "camelCase")]
24pub struct PatchRecord {
25 pub uuid: String,
26 pub exported_at: String,
27 pub files: HashMap<String, PatchFileInfo>,
29 pub vulnerabilities: HashMap<String, VulnerabilityInfo>,
31 pub description: String,
32 pub license: String,
33 pub tier: String,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
39pub struct PatchManifest {
40 pub patches: HashMap<String, PatchRecord>,
42}
43
44impl PatchManifest {
45 pub fn new() -> Self {
47 Self {
48 patches: HashMap::new(),
49 }
50 }
51}
52
53impl Default for PatchManifest {
54 fn default() -> Self {
55 Self::new()
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn test_empty_manifest_roundtrip() {
65 let manifest = PatchManifest::new();
66 let json = serde_json::to_string_pretty(&manifest).unwrap();
67 let parsed: PatchManifest = serde_json::from_str(&json).unwrap();
68 assert_eq!(parsed.patches.len(), 0);
69 }
70
71 #[test]
72 fn test_manifest_with_patch_roundtrip() {
73 let json = r#"{
74 "patches": {
75 "pkg:npm/simplehttpserver@0.0.6": {
76 "uuid": "12345678-1234-1234-1234-123456789abc",
77 "exportedAt": "2024-01-15T10:00:00Z",
78 "files": {
79 "package/lib/server.js": {
80 "beforeHash": "aaaa000000000000000000000000000000000000000000000000000000000000",
81 "afterHash": "bbbb000000000000000000000000000000000000000000000000000000000000"
82 }
83 },
84 "vulnerabilities": {
85 "GHSA-jrhj-2j3q-xf3v": {
86 "cves": ["CVE-2024-1234"],
87 "summary": "Path traversal vulnerability",
88 "severity": "high",
89 "description": "A path traversal vulnerability exists in simplehttpserver"
90 }
91 },
92 "description": "Fix path traversal vulnerability",
93 "license": "MIT",
94 "tier": "free"
95 }
96 }
97}"#;
98
99 let manifest: PatchManifest = serde_json::from_str(json).unwrap();
100 assert_eq!(manifest.patches.len(), 1);
101
102 let patch = manifest.patches.get("pkg:npm/simplehttpserver@0.0.6").unwrap();
103 assert_eq!(patch.uuid, "12345678-1234-1234-1234-123456789abc");
104 assert_eq!(patch.files.len(), 1);
105 assert_eq!(patch.vulnerabilities.len(), 1);
106 assert_eq!(patch.tier, "free");
107
108 let file_info = patch.files.get("package/lib/server.js").unwrap();
109 assert_eq!(
110 file_info.before_hash,
111 "aaaa000000000000000000000000000000000000000000000000000000000000"
112 );
113
114 let vuln = patch.vulnerabilities.get("GHSA-jrhj-2j3q-xf3v").unwrap();
115 assert_eq!(vuln.cves, vec!["CVE-2024-1234"]);
116 assert_eq!(vuln.severity, "high");
117
118 let serialized = serde_json::to_string_pretty(&manifest).unwrap();
120 let reparsed: PatchManifest = serde_json::from_str(&serialized).unwrap();
121 assert_eq!(manifest, reparsed);
122 }
123
124 #[test]
125 fn test_camel_case_serialization() {
126 let file_info = PatchFileInfo {
127 before_hash: "aaa".to_string(),
128 after_hash: "bbb".to_string(),
129 };
130 let json = serde_json::to_string(&file_info).unwrap();
131 assert!(json.contains("beforeHash"));
132 assert!(json.contains("afterHash"));
133 assert!(!json.contains("before_hash"));
134 assert!(!json.contains("after_hash"));
135 }
136
137 #[test]
138 fn test_patch_record_camel_case() {
139 let record = PatchRecord {
140 uuid: "test-uuid".to_string(),
141 exported_at: "2024-01-01T00:00:00Z".to_string(),
142 files: HashMap::new(),
143 vulnerabilities: HashMap::new(),
144 description: "test".to_string(),
145 license: "MIT".to_string(),
146 tier: "free".to_string(),
147 };
148 let json = serde_json::to_string(&record).unwrap();
149 assert!(json.contains("exportedAt"));
150 assert!(!json.contains("exported_at"));
151 }
152}