Skip to main content

phalus/
lib.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5pub mod agents;
6pub mod audit;
7pub mod cache;
8pub mod config;
9pub mod docs;
10pub mod firewall;
11pub mod license;
12pub mod manifest;
13pub mod pipeline;
14pub mod registry;
15pub mod sbom;
16pub mod scan;
17pub mod store;
18pub mod validator;
19pub mod web;
20
21// ---------------------------------------------------------------------------
22// Ecosystem
23// ---------------------------------------------------------------------------
24
25#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
26#[serde(rename_all = "lowercase")]
27pub enum Ecosystem {
28    Npm,
29    PyPI,
30    Crates,
31    Go,
32}
33
34impl std::fmt::Display for Ecosystem {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        match self {
37            Ecosystem::Npm => write!(f, "npm"),
38            Ecosystem::PyPI => write!(f, "pypi"),
39            Ecosystem::Crates => write!(f, "crates"),
40            Ecosystem::Go => write!(f, "go"),
41        }
42    }
43}
44
45// ---------------------------------------------------------------------------
46// Manifest types
47// ---------------------------------------------------------------------------
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct PackageRef {
51    pub name: String,
52    pub version_constraint: String,
53    pub ecosystem: Ecosystem,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct ParsedManifest {
58    pub manifest_type: String,
59    pub packages: Vec<PackageRef>,
60}
61
62// ---------------------------------------------------------------------------
63// Registry / metadata types
64// ---------------------------------------------------------------------------
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct PackageMetadata {
68    pub name: String,
69    pub version: String,
70    pub ecosystem: Ecosystem,
71    pub description: Option<String>,
72    pub license: Option<String>,
73    pub repository_url: Option<String>,
74    pub homepage_url: Option<String>,
75    pub unpacked_size: Option<u64>,
76    pub registry_url: String,
77}
78
79// ---------------------------------------------------------------------------
80// Documentation types
81// ---------------------------------------------------------------------------
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct DocEntry {
85    pub name: String,
86    pub content: String,
87    pub source_url: Option<String>,
88    pub content_hash: String,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct Documentation {
93    pub package: PackageMetadata,
94    pub documents: Vec<DocEntry>,
95    pub content_hash: String,
96}
97
98// ---------------------------------------------------------------------------
99// CSP (Clean-Shot Package) types
100// ---------------------------------------------------------------------------
101
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct CspDocument {
104    pub filename: String,
105    pub content: String,
106    pub content_hash: String,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct CspSpec {
111    pub package_name: String,
112    pub package_version: String,
113    pub documents: Vec<CspDocument>,
114    pub generated_at: DateTime<Utc>,
115}
116
117// ---------------------------------------------------------------------------
118// Implementation type
119// ---------------------------------------------------------------------------
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct Implementation {
123    pub package_name: String,
124    pub files: HashMap<String, String>,
125    pub target_language: String,
126}
127
128// ---------------------------------------------------------------------------
129// Validation types
130// ---------------------------------------------------------------------------
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct SimilarityReport {
134    pub token_similarity: f64,
135    pub name_overlap: f64,
136    pub string_overlap: f64,
137    pub structural_similarity: f64,
138    pub overall_score: f64,
139    pub threshold: f64,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
143#[serde(rename_all = "lowercase")]
144pub enum Verdict {
145    Pass,
146    Fail,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct ValidationReport {
151    pub package: PackageMetadata,
152    pub syntax_ok: bool,
153    pub tests_passed: u32,
154    pub tests_failed: u32,
155    pub api_coverage: f64,
156    pub license_ok: bool,
157    pub similarity: SimilarityReport,
158    pub verdict: Verdict,
159}
160
161// ---------------------------------------------------------------------------
162// License template resolution
163// ---------------------------------------------------------------------------
164
165pub fn resolve_license_text(license_id: &str, year: &str, holder: &str) -> Option<String> {
166    let template = match license_id {
167        "mit" => include_str!("../licenses/mit.txt"),
168        "apache-2.0" => include_str!("../licenses/apache-2.0.txt"),
169        "bsd-2" => include_str!("../licenses/bsd-2.txt"),
170        "bsd-3" => include_str!("../licenses/bsd-3.txt"),
171        "isc" => include_str!("../licenses/isc.txt"),
172        "unlicense" => include_str!("../licenses/unlicense.txt"),
173        "cc0" => include_str!("../licenses/cc0.txt"),
174        _ => return None,
175    };
176    Some(template.replace("{year}", year).replace("{holder}", holder))
177}
178
179// ---------------------------------------------------------------------------
180// License scanning types (Phase 1)
181// ---------------------------------------------------------------------------
182
183/// SPDX-canonical license classification bucket.
184#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
185#[serde(rename_all = "kebab-case")]
186pub enum LicenseClass {
187    Permissive,
188    CopyleftWeak,
189    CopyleftStrong,
190    Proprietary,
191    Unknown,
192}
193
194impl std::fmt::Display for LicenseClass {
195    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196        match self {
197            LicenseClass::Permissive => write!(f, "permissive"),
198            LicenseClass::CopyleftWeak => write!(f, "copyleft-weak"),
199            LicenseClass::CopyleftStrong => write!(f, "copyleft-strong"),
200            LicenseClass::Proprietary => write!(f, "proprietary"),
201            LicenseClass::Unknown => write!(f, "unknown"),
202        }
203    }
204}
205
206/// A single package discovered during a scan with its resolved license.
207#[derive(Debug, Clone, Serialize, Deserialize)]
208pub struct ScannedPackage {
209    pub name: String,
210    pub version: String,
211    pub ecosystem: Ecosystem,
212    /// Raw license string as found in the manifest or registry.
213    pub raw_license: Option<String>,
214    /// Normalized SPDX identifier (e.g. "MIT", "Apache-2.0").
215    pub spdx_license: Option<String>,
216    /// High-level classification bucket.
217    pub classification: LicenseClass,
218    /// Where the license information came from ("manifest", "registry", "sbom").
219    pub source: String,
220}
221
222/// The result of a single scan run.
223#[derive(Debug, Clone, Serialize, Deserialize)]
224pub struct ScanResult {
225    pub id: String,
226    pub path: String,
227    pub scanned_at: DateTime<Utc>,
228    pub packages: Vec<ScannedPackage>,
229    pub manifest_files: Vec<String>,
230    pub sbom_files: Vec<String>,
231}
232
233// ---------------------------------------------------------------------------
234// Mode / language enums
235// ---------------------------------------------------------------------------
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
238#[serde(rename_all = "lowercase")]
239pub enum IsolationMode {
240    Context,
241    Process,
242    Container,
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize)]
246#[serde(rename_all = "lowercase")]
247pub enum TargetLanguage {
248    Same,
249    Rust,
250    Go,
251    Python,
252    TypeScript,
253}
254
255impl std::fmt::Display for TargetLanguage {
256    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
257        match self {
258            TargetLanguage::Same => write!(f, "same"),
259            TargetLanguage::Rust => write!(f, "rust"),
260            TargetLanguage::Go => write!(f, "go"),
261            TargetLanguage::Python => write!(f, "python"),
262            TargetLanguage::TypeScript => write!(f, "typescript"),
263        }
264    }
265}