Skip to main content

omena_bridge/
style_resolution.rs

1use std::{
2    collections::{BTreeMap, BTreeSet, VecDeque},
3    ffi::OsString,
4    fs,
5    path::{Component, Path, PathBuf},
6    sync::{Mutex, OnceLock},
7};
8
9use crate::bundler_config_alias::load_omena_bridge_workspace_bundler_path_alias_mappings;
10use crate::external_sif_signature::verify_omena_external_sif_keyless_bundle;
11use omena_resolver::{
12    OmenaResolverBundlerPathAliasMappingV0, OmenaResolverStyleModuleConfirmationOptionsV0,
13    OmenaResolverStyleModuleDiskCandidateIdentityV0, OmenaResolverStylePackageManifestV0,
14    OmenaResolverTsconfigPathMappingV0,
15    collect_omena_resolver_style_module_source_candidates_with_path_mappings,
16    confirm_omena_resolver_style_module_candidate_with_options,
17    is_omena_resolver_indexable_style_module_path,
18    normalize_omena_resolver_style_module_source_for_routing,
19};
20use omena_sif::{
21    OMENA_SIF_PUBLISHED_ATTESTATION_SUBJECT_PRODUCT_V1,
22    OMENA_SIF_PUBLISHED_ATTESTATION_SUBJECT_SCHEMA_VERSION_V1,
23    OMENA_SIF_SHARD_TRUST_ENVELOPE_PRODUCT_V1, OMENA_SIF_SHARD_TRUST_ENVELOPE_SCHEMA_VERSION_V1,
24    OMENA_SIF_SHARD_VERDICT_DIR_V1, OmenaLifExportsV1, OmenaSifPublishedAttestationSubjectV1,
25    OmenaSifShardLockBindingV1, OmenaSifShardRecordedVerdictV1, OmenaSifShardTrustEnvelopeV1,
26    OmenaSifSourceSyntaxV1, OmenaSifStaticGeneratorInputV1, OmenaSifTrustTierV1, OmenaSifV1,
27    compute_omena_sif_artifact_hash_v1, compute_omena_sif_leaf_hash_v1,
28    compute_omena_sif_shard_recorded_verdict_address_v1, generate_static_omena_lif_exports_v1,
29    generate_static_omena_sif_v1, read_omena_sif_json_v1,
30    read_omena_sif_shard_recorded_verdict_json_v1, read_omena_sif_shard_trust_envelope_json_v1,
31    validate_omena_sif_published_attestation_subject_v1, write_omena_canonical_json_bytes_v1,
32    write_omena_sif_json_v1, write_omena_sif_published_attestation_subject_json_v1,
33};
34use serde::Serialize;
35use serde_json::{Value, json};
36
37const WORKSPACE_PACKAGE_MANIFEST_SCAN_LIMIT: usize = 1024;
38const EXTERNAL_SIF_CACHE_SCHEMA_VERSION: &str = "1";
39const EXTERNAL_SIF_CACHE_LEGACY_SCHEMA_VERSION: &str = "0";
40const EXTERNAL_SIF_CACHE_KEY_SCHEMA_VERSION: &str = "0";
41const EXTERNAL_SIF_CACHE_PRODUCT: &str = "omena-bridge.external-sif-cache-shard";
42const EXTERNAL_SIF_CACHE_DIR: &str = "external-sif-v0";
43const EXTERNAL_SIF_CACHE_ENV_KILL_SWITCH: &str = "OMENA_BRIDGE_EXTERNAL_SIF_CACHE";
44const EXTERNAL_SIF_CACHE_MAX_MEMORY_ENTRIES: usize = 256;
45const EXTERNAL_SIF_CACHE_MAX_SHARDS: usize = 2048;
46const EXTERNAL_SIF_CACHE_MAX_TOTAL_BYTES: u64 = 256 * 1024 * 1024;
47const EXTERNAL_SIF_CACHE_MAX_SHARD_BYTES: u64 = 8 * 1024 * 1024;
48const EXTERNAL_SIF_RECORDED_BUNDLE_DIR_V1: &str = "bundles-v1";
49const EXTERNAL_SIF_RECORDED_BUNDLE_SUFFIX_V1: &str = ".sigstore.json";
50const EXTERNAL_SIF_RECORDED_BUNDLE_MAX_BYTES: u64 = 4 * 1024 * 1024;
51const EXTERNAL_SIF_RECORDED_VERDICT_SCAN_LIMIT: usize = 4096;
52const WORKSPACE_STYLE_PATH_IDENTITY_SCAN_LIMIT: usize = 4096;
53const WORKSPACE_STYLE_PATH_IDENTITY_MAX_DEPTH: usize = 8;
54
55static EXTERNAL_SIF_MEMORY_CACHE: OnceLock<
56    Mutex<BTreeMap<String, OmenaBridgeExternalSifWithTrustV1>>,
57> = OnceLock::new();
58
59#[cfg(test)]
60static EXTERNAL_SIF_LOCAL_REGENERATION_COUNTS: OnceLock<Mutex<BTreeMap<String, usize>>> =
61    OnceLock::new();
62
63#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
64#[serde(rename_all = "camelCase")]
65pub struct OmenaBridgeStyleResolutionSummaryV0 {
66    pub schema_version: &'static str,
67    pub product: &'static str,
68    pub owner_crate: &'static str,
69    pub resolver_name: &'static str,
70    pub supported_specifier_kinds: Vec<&'static str>,
71    pub candidate_extensions: Vec<&'static str>,
72    pub request_path_policy: Vec<&'static str>,
73}
74
75#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
76#[serde(rename_all = "camelCase")]
77pub struct OmenaBridgeStyleResolutionInputsV0 {
78    pub package_manifests: Vec<OmenaResolverStylePackageManifestV0>,
79    pub tsconfig_path_mappings: Vec<OmenaResolverTsconfigPathMappingV0>,
80    pub bundler_path_mappings: Vec<OmenaResolverBundlerPathAliasMappingV0>,
81    #[serde(skip_serializing_if = "Vec::is_empty")]
82    pub disk_style_path_identities: Vec<OmenaResolverStyleModuleDiskCandidateIdentityV0>,
83}
84
85#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
86#[serde(rename_all = "camelCase")]
87pub struct OmenaBridgeExternalSifCacheContextV0 {
88    #[serde(skip_serializing_if = "Option::is_none")]
89    pub freshness_fingerprint: Option<String>,
90}
91
92#[derive(Debug, Clone, PartialEq, Eq)]
93pub struct OmenaBridgeExternalSifStorageV0 {
94    workspace_cache_root: PathBuf,
95    workspace_identity: Option<String>,
96    recorded_verdict_dir: Option<PathBuf>,
97}
98
99impl OmenaBridgeExternalSifStorageV0 {
100    pub fn from_workspace_cache_root(workspace_cache_root: PathBuf) -> Self {
101        Self {
102            recorded_verdict_dir: Some(workspace_cache_root.join(OMENA_SIF_SHARD_VERDICT_DIR_V1)),
103            workspace_cache_root,
104            workspace_identity: None,
105        }
106    }
107
108    pub fn from_workspace_cache_root_and_identity(
109        workspace_cache_root: PathBuf,
110        workspace_identity: impl Into<String>,
111    ) -> Self {
112        Self {
113            recorded_verdict_dir: Some(workspace_cache_root.join(OMENA_SIF_SHARD_VERDICT_DIR_V1)),
114            workspace_cache_root,
115            workspace_identity: Some(workspace_identity.into()),
116        }
117    }
118
119    pub fn workspace_cache_root(&self) -> &Path {
120        self.workspace_cache_root.as_path()
121    }
122
123    pub fn with_recorded_verdict_dir(mut self, recorded_verdict_dir: PathBuf) -> Self {
124        self.recorded_verdict_dir = Some(recorded_verdict_dir);
125        self
126    }
127
128    pub fn recorded_verdict_dir(&self) -> Option<&Path> {
129        self.recorded_verdict_dir.as_deref()
130    }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
134#[serde(rename_all = "camelCase")]
135pub enum OmenaBridgeExternalSifTrustSourceV1 {
136    RecordedVerdict,
137    UnsignedLegacy,
138}
139
140#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
141#[serde(rename_all = "camelCase")]
142pub struct OmenaBridgeExternalSifWithTrustV1 {
143    pub sif: OmenaSifV1,
144    pub trust_envelope: OmenaSifShardTrustEnvelopeV1,
145    pub trust_source: OmenaBridgeExternalSifTrustSourceV1,
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
149pub enum OmenaBridgeExternalSifShardRefusalV1 {
150    ShardIdentityMismatch,
151    CanonicalUrlMismatch,
152    LocalRegenerationMismatch,
153    PayloadDigestMismatch,
154    MalformedSif,
155    MissingTrustEnvelope,
156    InvalidTrustEnvelope,
157    LockBindingMismatch,
158    MissingRecordedVerdict,
159    RecordedVerdictMismatch,
160    TierAboveRecordedVerdict,
161    RecordedVerdictDowngrade,
162    RecordedVerdictSignatureVerificationFailed,
163}
164
165impl OmenaBridgeExternalSifShardRefusalV1 {
166    pub fn code(self) -> &'static str {
167        match self {
168            Self::ShardIdentityMismatch => "shardIdentityMismatch",
169            Self::CanonicalUrlMismatch => "canonicalUrlMismatch",
170            Self::LocalRegenerationMismatch => "localRegenerationMismatch",
171            Self::PayloadDigestMismatch => "payloadDigestMismatch",
172            Self::MalformedSif => "malformedSif",
173            Self::MissingTrustEnvelope => "missingTrustEnvelope",
174            Self::InvalidTrustEnvelope => "invalidTrustEnvelope",
175            Self::LockBindingMismatch => "lockBindingMismatch",
176            Self::MissingRecordedVerdict => "missingRecordedVerdict",
177            Self::RecordedVerdictMismatch => "recordedVerdictMismatch",
178            Self::TierAboveRecordedVerdict => "tierAboveRecordedVerdict",
179            Self::RecordedVerdictDowngrade => "recordedVerdictDowngrade",
180            Self::RecordedVerdictSignatureVerificationFailed => {
181                "recordedVerdictSignatureVerificationFailed"
182            }
183        }
184    }
185}
186
187pub fn summarize_omena_bridge_style_resolution_boundary() -> OmenaBridgeStyleResolutionSummaryV0 {
188    OmenaBridgeStyleResolutionSummaryV0 {
189        schema_version: "0",
190        product: "omena-bridge.style-resolution",
191        owner_crate: "omena-bridge",
192        resolver_name: "style-import-specifier-resolver",
193        supported_specifier_kinds: vec![
194            "relative",
195            "tsconfigPaths",
196            "jsconfigPaths",
197            "bundlerAliases",
198            "npmPackages",
199            "packageImports",
200        ],
201        candidate_extensions: vec!["scss", "sass", "css", "less"],
202        request_path_policy: vec![
203            "resolverConsumesSourceUriWorkspaceUriAndRawSpecifier",
204            "relativeSpecifierExpandsStyleModuleCandidates",
205            "pathAliasResolutionUsesNearestWorkspaceTsconfigOrJsconfig",
206            "pathAliasResolutionFollowsRelativeTsconfigExtends",
207            "bundlerAliasResolutionUsesLiteralViteWebpackConfig",
208            "packageSpecifierResolutionUsesOmenaResolver",
209            "fileUriOutputIsPercentEncoded",
210            "lspServerOwnsOnlyDocumentRoutingAndUriRangeMapping",
211        ],
212    }
213}
214
215pub fn resolve_omena_bridge_style_uri_for_specifier(
216    source_uri: &str,
217    workspace_folder_uri: Option<&str>,
218    specifier: &str,
219) -> Option<String> {
220    resolve_omena_bridge_style_uri_for_specifier_with_package_manifests(
221        source_uri,
222        workspace_folder_uri,
223        specifier,
224        &[],
225    )
226}
227
228pub fn resolve_omena_bridge_style_uri_for_specifier_with_package_manifests(
229    source_uri: &str,
230    workspace_folder_uri: Option<&str>,
231    specifier: &str,
232    configured_package_manifests: &[OmenaResolverStylePackageManifestV0],
233) -> Option<String> {
234    let source_path = normalize_path(file_uri_to_path(source_uri)?);
235    let workspace_path = workspace_folder_uri
236        .and_then(file_uri_to_path)
237        .map(normalize_path);
238    let package_manifests = merged_package_manifests_for_request(
239        source_path.parent(),
240        workspace_path.as_deref(),
241        specifier,
242        configured_package_manifests,
243    );
244    let inputs = OmenaBridgeStyleResolutionInputsV0 {
245        package_manifests,
246        tsconfig_path_mappings: tsconfig_path_mappings_for_workspace(workspace_path.as_deref())
247            .unwrap_or_default(),
248        bundler_path_mappings: load_omena_bridge_workspace_bundler_path_alias_mappings(
249            workspace_path.as_deref(),
250        ),
251        disk_style_path_identities: workspace_path
252            .as_deref()
253            .map(workspace_style_path_identities)
254            .unwrap_or_default(),
255    };
256    resolve_omena_bridge_style_uri_for_specifier_with_resolution_inputs(
257        source_uri,
258        workspace_folder_uri,
259        specifier,
260        &inputs,
261    )
262}
263
264pub fn resolve_omena_bridge_style_uri_for_specifier_with_resolution_inputs(
265    source_uri: &str,
266    _workspace_folder_uri: Option<&str>,
267    specifier: &str,
268    resolution_inputs: &OmenaBridgeStyleResolutionInputsV0,
269) -> Option<String> {
270    let source_path = normalize_path(file_uri_to_path(source_uri)?);
271    let source_path_text = source_path.to_string_lossy().to_string();
272    let routing_specifier = normalize_omena_resolver_style_module_source_for_routing(specifier);
273    let requires_existing_candidate = (package_name_from_specifier(routing_specifier).is_some()
274        || is_package_import_specifier(routing_specifier))
275        && !resolution_inputs
276            .tsconfig_path_mappings
277            .iter()
278            .any(|mapping| {
279                tsconfig_path_pattern_matches(mapping.pattern.as_str(), routing_specifier)
280            })
281        && !resolution_inputs
282            .bundler_path_mappings
283            .iter()
284            .any(|mapping| {
285                bundler_path_alias_pattern_matches(mapping.pattern.as_str(), routing_specifier)
286            });
287    let candidates = collect_omena_resolver_style_module_source_candidates_with_path_mappings(
288        source_path_text.as_str(),
289        specifier,
290        resolution_inputs.package_manifests.as_slice(),
291        resolution_inputs.bundler_path_mappings.as_slice(),
292        resolution_inputs.tsconfig_path_mappings.as_slice(),
293    );
294
295    style_uri_for_resolver_candidates(
296        candidates.as_slice(),
297        resolution_inputs.disk_style_path_identities.as_slice(),
298        requires_existing_candidate,
299    )
300}
301
302/// Bridges the resolver→generator hop in-process: takes a resolved external
303/// style module entry (the `file://` URI returned by
304/// `resolve_omena_bridge_style_uri_for_specifier*`, or a plain filesystem
305/// path) and produces an [`OmenaSifV1`] by reading the entry's source and
306/// running the static SIF generator.
307///
308/// The returned SIF's `canonical_url` matches the resolved entry's `file://`
309/// URI so the query layer can pair it against import targets. The CLI converts
310/// each result into an `OmenaQueryExternalSifInputV0` without a JSON round-trip.
311///
312/// Errors gracefully (never panics) when the path is unresolvable, missing, or
313/// unreadable.
314pub fn generate_omena_bridge_sif_for_resolved_style_path(
315    resolved_path: &str,
316) -> Result<OmenaSifV1, String> {
317    generate_omena_bridge_sif_for_resolved_style_path_with_cache_context(
318        resolved_path,
319        &OmenaBridgeExternalSifCacheContextV0::default(),
320    )
321}
322
323pub fn generate_omena_bridge_lif_exports_for_resolved_style_path(
324    resolved_path: &str,
325) -> Result<OmenaLifExportsV1, String> {
326    let raw_path = raw_resolved_style_entry_path(resolved_path)
327        .ok_or_else(|| format!("unresolvable style module entry path: {resolved_path}"))?;
328    let path = normalize_path(raw_path);
329    let source = fs::read_to_string(path.as_path()).map_err(|error| {
330        format!(
331            "failed to read resolved style module {}: {error}",
332            path.to_string_lossy()
333        )
334    })?;
335    let syntax = infer_omena_bridge_sif_source_syntax(path.as_path());
336    Ok(generate_static_omena_lif_exports_v1(
337        OmenaSifStaticGeneratorInputV1 {
338            canonical_url: path_to_file_uri(path.as_path()).as_str(),
339            source: source.as_str(),
340            syntax,
341        },
342    ))
343}
344
345pub fn generate_omena_bridge_sif_for_resolved_style_path_with_cache_context(
346    resolved_path: &str,
347    cache_context: &OmenaBridgeExternalSifCacheContextV0,
348) -> Result<OmenaSifV1, String> {
349    generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
350        resolved_path,
351        cache_context,
352        None,
353    )
354}
355
356pub fn generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
357    resolved_path: &str,
358    cache_context: &OmenaBridgeExternalSifCacheContextV0,
359    cache_storage: Option<&OmenaBridgeExternalSifStorageV0>,
360) -> Result<OmenaSifV1, String> {
361    generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_storage_and_trust(
362        resolved_path,
363        cache_context,
364        cache_storage,
365    )
366    .map(|result| result.sif)
367}
368
369pub fn generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_storage_and_trust(
370    resolved_path: &str,
371    cache_context: &OmenaBridgeExternalSifCacheContextV0,
372    cache_storage: Option<&OmenaBridgeExternalSifStorageV0>,
373) -> Result<OmenaBridgeExternalSifWithTrustV1, String> {
374    generate_omena_bridge_sif_for_resolved_style_path_with_canonical_url_impl(
375        resolved_path,
376        None,
377        cache_context,
378        cache_storage,
379    )
380}
381
382pub fn generate_omena_bridge_sif_for_resolved_style_path_with_canonical_url_cache_context_storage_and_trust(
383    resolved_path: &str,
384    canonical_url: &str,
385    cache_context: &OmenaBridgeExternalSifCacheContextV0,
386    cache_storage: Option<&OmenaBridgeExternalSifStorageV0>,
387) -> Result<OmenaBridgeExternalSifWithTrustV1, String> {
388    if canonical_url.trim().is_empty() {
389        return Err("external SIF canonical URL must not be empty".to_string());
390    }
391    generate_omena_bridge_sif_for_resolved_style_path_with_canonical_url_impl(
392        resolved_path,
393        Some(canonical_url),
394        cache_context,
395        cache_storage,
396    )
397}
398
399fn generate_omena_bridge_sif_for_resolved_style_path_with_canonical_url_impl(
400    resolved_path: &str,
401    canonical_url_override: Option<&str>,
402    cache_context: &OmenaBridgeExternalSifCacheContextV0,
403    cache_storage: Option<&OmenaBridgeExternalSifStorageV0>,
404) -> Result<OmenaBridgeExternalSifWithTrustV1, String> {
405    let raw_path = raw_resolved_style_entry_path(resolved_path)
406        .ok_or_else(|| format!("unresolvable style module entry path: {resolved_path}"))?;
407    let path = normalize_path(raw_path.clone());
408    let canonical_url = canonical_url_override
409        .map(ToOwned::to_owned)
410        .unwrap_or_else(|| path_to_file_uri(path.as_path()));
411    let source_bytes = fs::read(path.as_path()).map_err(|error| {
412        format!(
413            "failed to read resolved style module {}: {error}",
414            path.to_string_lossy()
415        )
416    })?;
417    let source_hash = compute_omena_sif_leaf_hash_v1(source_bytes.as_slice())
418        .as_str()
419        .to_string();
420    let resolved_base_dir = path
421        .parent()
422        .map(|base_dir| base_dir.to_string_lossy().to_string())
423        .unwrap_or_default();
424    let cache_key = external_sif_cache_key(
425        source_hash.as_str(),
426        resolved_base_dir.as_str(),
427        canonical_url.as_str(),
428        cache_context.freshness_fingerprint.as_deref(),
429    );
430    let cache_enabled = !external_sif_cache_kill_switch_engaged();
431    let cache_dir = cache_enabled
432        .then(|| external_sif_cache_dir_for_path(raw_path.as_path(), cache_storage))
433        .flatten();
434    let cache_workspace_identity = cache_storage
435        .and_then(|storage| storage.workspace_identity.clone())
436        .or_else(|| {
437            crate::cache_root::external_sif_workspace_root(raw_path.as_path())
438                .map(|root| root.to_string_lossy().into_owned())
439        });
440    let recorded_verdict_dir =
441        external_sif_recorded_verdict_dir_for_path(raw_path.as_path(), cache_storage);
442    let memory_cache_key = external_sif_memory_cache_key(cache_key.as_str(), cache_dir.as_deref());
443    if cache_enabled
444        && let Some(result) = load_external_sif_from_memory_cache(memory_cache_key.as_str())
445    {
446        if result.sif.canonical_url == canonical_url
447            && result.sif.fingerprints.leaf_hash.as_str() == source_hash
448        {
449            let result = external_sif_result_with_recorded_verdict(
450                result.sif,
451                recorded_verdict_dir.as_deref(),
452            )?;
453            store_external_sif_in_memory_cache(memory_cache_key.clone(), result.clone());
454            return Ok(result);
455        }
456        remove_external_sif_from_memory_cache(memory_cache_key.as_str());
457    }
458    let source = String::from_utf8(source_bytes).map_err(|error| {
459        format!(
460            "failed to decode resolved style module {} as utf-8: {error}",
461            path.to_string_lossy()
462        )
463    })?;
464    let syntax = infer_omena_bridge_sif_source_syntax(path.as_path());
465    let locally_regenerated_sif =
466        generate_local_external_sif(canonical_url.as_str(), source.as_str(), syntax)?;
467    if cache_enabled
468        && let Some(cache_dir) = cache_dir.as_deref()
469        && let Some(result) = load_external_sif_cache_shard(
470            cache_dir,
471            cache_key.as_str(),
472            canonical_url.as_str(),
473            source_hash.as_str(),
474            resolved_base_dir.as_str(),
475            recorded_verdict_dir.as_deref(),
476            &locally_regenerated_sif,
477        )
478    {
479        store_external_sif_in_memory_cache(memory_cache_key.clone(), result.clone());
480        return Ok(result);
481    }
482    let result = external_sif_result_with_recorded_verdict(
483        locally_regenerated_sif,
484        recorded_verdict_dir.as_deref(),
485    )?;
486    if cache_enabled {
487        store_external_sif_in_memory_cache(memory_cache_key, result.clone());
488        if let Some(cache_dir) = cache_dir.as_deref() {
489            store_external_sif_cache_shard(
490                cache_dir,
491                cache_key.as_str(),
492                canonical_url.as_str(),
493                source_hash.as_str(),
494                resolved_base_dir.as_str(),
495                &result,
496                cache_workspace_identity.as_deref(),
497            );
498        }
499    }
500    Ok(result)
501}
502
503fn generate_local_external_sif(
504    canonical_url: &str,
505    source: &str,
506    syntax: OmenaSifSourceSyntaxV1,
507) -> Result<OmenaSifV1, String> {
508    #[cfg(test)]
509    if let Ok(mut counts) = EXTERNAL_SIF_LOCAL_REGENERATION_COUNTS
510        .get_or_init(|| Mutex::new(BTreeMap::new()))
511        .lock()
512    {
513        let count = counts.entry(canonical_url.to_string()).or_default();
514        *count = count.saturating_add(1);
515    }
516    generate_static_omena_sif_v1(OmenaSifStaticGeneratorInputV1 {
517        canonical_url,
518        source,
519        syntax,
520    })
521    .map_err(|error| format!("failed to generate SIF for {canonical_url}: {error}"))
522}
523
524fn raw_resolved_style_entry_path(resolved_path: &str) -> Option<PathBuf> {
525    let path = if resolved_path.starts_with("file://") {
526        file_uri_to_path(resolved_path)?
527    } else if resolved_path.is_empty() {
528        return None;
529    } else {
530        PathBuf::from(resolved_path)
531    };
532    Some(normalize_path_lexical(path))
533}
534
535fn external_sif_cache_key(
536    source_hash: &str,
537    resolved_base_dir: &str,
538    canonical_url: &str,
539    freshness_fingerprint: Option<&str>,
540) -> String {
541    external_sif_cache_key_with_crate_version(
542        source_hash,
543        resolved_base_dir,
544        canonical_url,
545        freshness_fingerprint,
546        env!("CARGO_PKG_VERSION"),
547    )
548}
549
550fn external_sif_cache_key_with_crate_version(
551    source_hash: &str,
552    resolved_base_dir: &str,
553    canonical_url: &str,
554    freshness_fingerprint: Option<&str>,
555    crate_version: &str,
556) -> String {
557    let input = json!({
558        "schemaVersion": EXTERNAL_SIF_CACHE_KEY_SCHEMA_VERSION,
559        "product": "omena-bridge.external-sif-cache-key",
560        "crateVersion": crate_version,
561        "sourceHash": source_hash,
562        "resolvedBaseDir": resolved_base_dir,
563        "canonicalUrl": canonical_url,
564        "freshnessFingerprint": freshness_fingerprint,
565    });
566    write_omena_canonical_json_bytes_v1(&input)
567        .map(|bytes| {
568            compute_omena_sif_leaf_hash_v1(bytes.as_slice())
569                .as_str()
570                .to_string()
571        })
572        .unwrap_or_else(|_| {
573            compute_omena_sif_leaf_hash_v1(
574                format!(
575                    "{crate_version}\0{source_hash}\0{resolved_base_dir}\0{canonical_url}\0{}",
576                    freshness_fingerprint.unwrap_or("")
577                )
578                .as_bytes(),
579            )
580            .as_str()
581            .to_string()
582        })
583}
584
585fn load_external_sif_from_memory_cache(key: &str) -> Option<OmenaBridgeExternalSifWithTrustV1> {
586    EXTERNAL_SIF_MEMORY_CACHE
587        .get_or_init(|| Mutex::new(BTreeMap::new()))
588        .lock()
589        .ok()?
590        .get(key)
591        .cloned()
592}
593
594fn store_external_sif_in_memory_cache(key: String, result: OmenaBridgeExternalSifWithTrustV1) {
595    let Ok(mut cache) = EXTERNAL_SIF_MEMORY_CACHE
596        .get_or_init(|| Mutex::new(BTreeMap::new()))
597        .lock()
598    else {
599        return;
600    };
601    cache.insert(key, result);
602    while cache.len() > EXTERNAL_SIF_CACHE_MAX_MEMORY_ENTRIES {
603        let Some(first_key) = cache.keys().next().cloned() else {
604            break;
605        };
606        cache.remove(first_key.as_str());
607    }
608}
609
610fn remove_external_sif_from_memory_cache(key: &str) {
611    let Ok(mut cache) = EXTERNAL_SIF_MEMORY_CACHE
612        .get_or_init(|| Mutex::new(BTreeMap::new()))
613        .lock()
614    else {
615        return;
616    };
617    cache.remove(key);
618}
619
620fn external_sif_memory_cache_key(key: &str, cache_dir: Option<&Path>) -> String {
621    cache_dir
622        .map(|cache_dir| format!("{}\0{key}", cache_dir.to_string_lossy()))
623        .unwrap_or_else(|| key.to_string())
624}
625
626fn external_sif_cache_dir_for_path(
627    path: &Path,
628    cache_storage: Option<&OmenaBridgeExternalSifStorageV0>,
629) -> Option<PathBuf> {
630    if let Some(cache_storage) = cache_storage {
631        return Some(
632            cache_storage
633                .workspace_cache_root()
634                .join(EXTERNAL_SIF_CACHE_DIR),
635        );
636    }
637    crate::cache_root::process_external_sif_cache_root(path)?
638        .workspace
639        .map(|root| root.join(EXTERNAL_SIF_CACHE_DIR))
640}
641
642fn external_sif_recorded_verdict_dir_for_path(
643    path: &Path,
644    cache_storage: Option<&OmenaBridgeExternalSifStorageV0>,
645) -> Option<PathBuf> {
646    if let Some(dir) = cache_storage.and_then(|storage| storage.recorded_verdict_dir()) {
647        return Some(dir.to_path_buf());
648    }
649    crate::cache_root::external_sif_workspace_root(path).map(|root| {
650        root.join(".cache")
651            .join("omena")
652            .join(OMENA_SIF_SHARD_VERDICT_DIR_V1)
653    })
654}
655
656fn external_sif_cache_shard_file_path(dir: &Path, key: &str) -> Option<PathBuf> {
657    let hex = key.strip_prefix("blake3:")?;
658    if hex.is_empty() || !hex.bytes().all(|byte| byte.is_ascii_hexdigit()) {
659        return None;
660    }
661    Some(dir.join(format!("{hex}.json")))
662}
663
664fn load_external_sif_cache_shard(
665    dir: &Path,
666    key: &str,
667    canonical_url: &str,
668    source_hash: &str,
669    resolved_base_dir: &str,
670    recorded_verdict_dir: Option<&Path>,
671    locally_regenerated_sif: &OmenaSifV1,
672) -> Option<OmenaBridgeExternalSifWithTrustV1> {
673    let shard_path = external_sif_cache_shard_file_path(dir, key)?;
674    let metadata = fs::metadata(shard_path.as_path()).ok()?;
675    if !metadata.is_file() || metadata.len() > EXTERNAL_SIF_CACHE_MAX_SHARD_BYTES {
676        let _ = fs::remove_file(shard_path.as_path());
677        return None;
678    }
679    let bytes = fs::read(shard_path.as_path()).ok()?;
680    let shard = serde_json::from_slice::<Value>(bytes.as_slice()).ok()?;
681    match validate_external_sif_cache_shard(
682        &shard,
683        key,
684        canonical_url,
685        source_hash,
686        resolved_base_dir,
687        recorded_verdict_dir,
688        locally_regenerated_sif,
689    ) {
690        Ok(result) => Some(result),
691        Err(_) => {
692            let _ = fs::remove_file(shard_path.as_path());
693            None
694        }
695    }
696}
697
698fn validate_external_sif_cache_shard(
699    shard: &Value,
700    key: &str,
701    canonical_url: &str,
702    source_hash: &str,
703    resolved_base_dir: &str,
704    recorded_verdict_dir: Option<&Path>,
705    locally_regenerated_sif: &OmenaSifV1,
706) -> Result<OmenaBridgeExternalSifWithTrustV1, OmenaBridgeExternalSifShardRefusalV1> {
707    let schema_version = shard.get("schemaVersion").and_then(Value::as_str);
708    if !matches!(
709        schema_version,
710        Some(EXTERNAL_SIF_CACHE_SCHEMA_VERSION | EXTERNAL_SIF_CACHE_LEGACY_SCHEMA_VERSION)
711    ) || shard.get("product").and_then(Value::as_str) != Some(EXTERNAL_SIF_CACHE_PRODUCT)
712        || shard.get("key").and_then(Value::as_str) != Some(key)
713        || shard.get("canonicalUrl").and_then(Value::as_str) != Some(canonical_url)
714        || shard.get("sourceHash").and_then(Value::as_str) != Some(source_hash)
715        || shard.get("resolvedBaseDir").and_then(Value::as_str) != Some(resolved_base_dir)
716    {
717        return Err(OmenaBridgeExternalSifShardRefusalV1::ShardIdentityMismatch);
718    }
719    let sif_json = shard
720        .get("sifJson")
721        .and_then(Value::as_str)
722        .ok_or(OmenaBridgeExternalSifShardRefusalV1::MalformedSif)?;
723    let payload_digest = compute_omena_sif_leaf_hash_v1(sif_json.as_bytes());
724    if shard.get("payloadDigest").and_then(Value::as_str) != Some(payload_digest.as_str()) {
725        return Err(OmenaBridgeExternalSifShardRefusalV1::PayloadDigestMismatch);
726    }
727    let sif = read_omena_sif_json_v1(sif_json)
728        .map_err(|_| OmenaBridgeExternalSifShardRefusalV1::MalformedSif)?;
729    if sif.canonical_url != canonical_url {
730        return Err(OmenaBridgeExternalSifShardRefusalV1::CanonicalUrlMismatch);
731    }
732    if sif != *locally_regenerated_sif {
733        return Err(OmenaBridgeExternalSifShardRefusalV1::LocalRegenerationMismatch);
734    }
735    let sif_hash = compute_omena_sif_artifact_hash_v1(&sif)
736        .map_err(|_| OmenaBridgeExternalSifShardRefusalV1::MalformedSif)?;
737    if schema_version == Some(EXTERNAL_SIF_CACHE_LEGACY_SCHEMA_VERSION) {
738        if has_recorded_shard_verdict_for_canonical_url(recorded_verdict_dir, canonical_url) {
739            return Err(OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictDowngrade);
740        }
741        return Ok(unsigned_external_sif_result(sif, payload_digest, sif_hash));
742    }
743    let envelope_value = shard
744        .get("trustEnvelope")
745        .ok_or(OmenaBridgeExternalSifShardRefusalV1::MissingTrustEnvelope)?;
746    let envelope_source = serde_json::to_string(envelope_value)
747        .map_err(|_| OmenaBridgeExternalSifShardRefusalV1::InvalidTrustEnvelope)?;
748    let envelope = read_omena_sif_shard_trust_envelope_json_v1(envelope_source.as_str())
749        .map_err(|_| OmenaBridgeExternalSifShardRefusalV1::InvalidTrustEnvelope)?;
750    if envelope.payload_digest != payload_digest {
751        return Err(OmenaBridgeExternalSifShardRefusalV1::PayloadDigestMismatch);
752    }
753    if envelope.lock_binding.canonical_url != canonical_url
754        || envelope.lock_binding.sif_hash != sif_hash
755    {
756        return Err(OmenaBridgeExternalSifShardRefusalV1::LockBindingMismatch);
757    }
758    if envelope.trust_tier < OmenaSifTrustTierV1::T2 {
759        if has_recorded_shard_verdict_for_canonical_url(recorded_verdict_dir, canonical_url) {
760            return Err(OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictDowngrade);
761        }
762        return Ok(OmenaBridgeExternalSifWithTrustV1 {
763            sif,
764            trust_envelope: envelope,
765            trust_source: OmenaBridgeExternalSifTrustSourceV1::UnsignedLegacy,
766        });
767    }
768    let verdict = load_recorded_shard_verdict(recorded_verdict_dir, canonical_url, &sif_hash)
769        .ok_or(OmenaBridgeExternalSifShardRefusalV1::MissingRecordedVerdict)?;
770    if envelope.trust_tier > verdict.trust_tier {
771        return Err(OmenaBridgeExternalSifShardRefusalV1::TierAboveRecordedVerdict);
772    }
773    if envelope.lock_binding.canonical_url != verdict.canonical_url
774        || envelope.lock_binding.sif_hash != verdict.sif_hash
775        || envelope.signature.as_ref() != Some(&verdict.signature)
776    {
777        return Err(OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictMismatch);
778    }
779    verify_recorded_shard_verdict(recorded_verdict_dir, &verdict)?;
780    Ok(OmenaBridgeExternalSifWithTrustV1 {
781        sif,
782        trust_envelope: envelope,
783        trust_source: OmenaBridgeExternalSifTrustSourceV1::RecordedVerdict,
784    })
785}
786
787fn store_external_sif_cache_shard(
788    dir: &Path,
789    key: &str,
790    canonical_url: &str,
791    source_hash: &str,
792    resolved_base_dir: &str,
793    result: &OmenaBridgeExternalSifWithTrustV1,
794    workspace_identity: Option<&str>,
795) {
796    let Ok(sif_json) = write_omena_sif_json_v1(&result.sif) else {
797        return;
798    };
799    let payload_digest = compute_omena_sif_leaf_hash_v1(sif_json.as_bytes())
800        .as_str()
801        .to_string();
802    let shard = json!({
803        "schemaVersion": EXTERNAL_SIF_CACHE_SCHEMA_VERSION,
804        "product": EXTERNAL_SIF_CACHE_PRODUCT,
805        "key": key,
806        "canonicalUrl": canonical_url,
807        "sourceHash": source_hash,
808        "resolvedBaseDir": resolved_base_dir,
809        "payloadDigest": payload_digest,
810        "trustEnvelope": result.trust_envelope,
811        "sifJson": sif_json,
812    });
813    let Ok(bytes) = write_omena_canonical_json_bytes_v1(&shard) else {
814        return;
815    };
816    if bytes.len() as u64 > EXTERNAL_SIF_CACHE_MAX_SHARD_BYTES {
817        return;
818    }
819    if write_external_sif_cache_shard_atomically(dir, key, bytes.as_slice()).is_ok() {
820        if let Some(workspace_identity) = workspace_identity {
821            crate::cache_root::ensure_omena_cache_root_attribution(dir, workspace_identity);
822        }
823        enforce_external_sif_cache_caps(dir);
824    }
825}
826
827fn external_sif_result_with_recorded_verdict(
828    sif: OmenaSifV1,
829    recorded_verdict_dir: Option<&Path>,
830) -> Result<OmenaBridgeExternalSifWithTrustV1, String> {
831    let sif_json = write_omena_sif_json_v1(&sif)
832        .map_err(|error| format!("failed to serialize generated SIF trust payload: {error}"))?;
833    let payload_digest = compute_omena_sif_leaf_hash_v1(sif_json.as_bytes());
834    let sif_hash = compute_omena_sif_artifact_hash_v1(&sif)
835        .map_err(|error| format!("failed to hash generated SIF trust payload: {error}"))?;
836    let verdict =
837        load_recorded_shard_verdict(recorded_verdict_dir, sif.canonical_url.as_str(), &sif_hash);
838    let verified_verdict = verdict
839        .filter(|verdict| verify_recorded_shard_verdict(recorded_verdict_dir, verdict).is_ok());
840    let (trust_tier, signature, trust_source) = match verified_verdict {
841        Some(verdict) => (
842            verdict.trust_tier,
843            Some(verdict.signature),
844            OmenaBridgeExternalSifTrustSourceV1::RecordedVerdict,
845        ),
846        None => (
847            OmenaSifTrustTierV1::T1,
848            None,
849            OmenaBridgeExternalSifTrustSourceV1::UnsignedLegacy,
850        ),
851    };
852    let trust_envelope = OmenaSifShardTrustEnvelopeV1 {
853        schema_version: OMENA_SIF_SHARD_TRUST_ENVELOPE_SCHEMA_VERSION_V1.to_string(),
854        product: OMENA_SIF_SHARD_TRUST_ENVELOPE_PRODUCT_V1.to_string(),
855        trust_tier,
856        payload_digest,
857        signature,
858        lock_binding: OmenaSifShardLockBindingV1 {
859            canonical_url: sif.canonical_url.clone(),
860            sif_hash,
861        },
862    };
863    omena_sif::validate_omena_sif_shard_trust_envelope_v1(&trust_envelope)
864        .map_err(|error| format!("generated SIF trust envelope is invalid: {error}"))?;
865    Ok(OmenaBridgeExternalSifWithTrustV1 {
866        sif,
867        trust_envelope,
868        trust_source,
869    })
870}
871
872fn unsigned_external_sif_result(
873    sif: OmenaSifV1,
874    payload_digest: omena_sif::OmenaSifDigestV1,
875    sif_hash: omena_sif::OmenaSifDigestV1,
876) -> OmenaBridgeExternalSifWithTrustV1 {
877    OmenaBridgeExternalSifWithTrustV1 {
878        trust_envelope: OmenaSifShardTrustEnvelopeV1 {
879            schema_version: OMENA_SIF_SHARD_TRUST_ENVELOPE_SCHEMA_VERSION_V1.to_string(),
880            product: OMENA_SIF_SHARD_TRUST_ENVELOPE_PRODUCT_V1.to_string(),
881            trust_tier: OmenaSifTrustTierV1::T1,
882            payload_digest,
883            signature: None,
884            lock_binding: OmenaSifShardLockBindingV1 {
885                canonical_url: sif.canonical_url.clone(),
886                sif_hash,
887            },
888        },
889        sif,
890        trust_source: OmenaBridgeExternalSifTrustSourceV1::UnsignedLegacy,
891    }
892}
893
894fn load_recorded_shard_verdict(
895    recorded_verdict_dir: Option<&Path>,
896    canonical_url: &str,
897    sif_hash: &omena_sif::OmenaSifDigestV1,
898) -> Option<OmenaSifShardRecordedVerdictV1> {
899    let verdict_dir = recorded_verdict_dir?;
900    let address =
901        compute_omena_sif_shard_recorded_verdict_address_v1(canonical_url, sif_hash).ok()?;
902    let hex = address.as_str().strip_prefix("blake3:")?;
903    let verdict_path = verdict_dir.join(format!("{hex}.json"));
904    let metadata = fs::metadata(verdict_path.as_path()).ok()?;
905    if !metadata.is_file() || metadata.len() > 1024 * 1024 {
906        return None;
907    }
908    let source = fs::read_to_string(verdict_path.as_path()).ok()?;
909    let verdict = read_omena_sif_shard_recorded_verdict_json_v1(source.as_str()).ok()?;
910    (verdict.canonical_url == canonical_url && verdict.sif_hash == *sif_hash).then_some(verdict)
911}
912
913fn has_recorded_shard_verdict_for_canonical_url(
914    recorded_verdict_dir: Option<&Path>,
915    canonical_url: &str,
916) -> bool {
917    let Some(verdict_dir) = recorded_verdict_dir else {
918        return false;
919    };
920    let entries = match fs::read_dir(verdict_dir) {
921        Ok(entries) => entries,
922        Err(error) if error.kind() == std::io::ErrorKind::NotFound => return false,
923        Err(_) => return true,
924    };
925    let mut verdict_file_count = 0usize;
926    for entry in entries {
927        let Ok(entry) = entry else {
928            return true;
929        };
930        let path = entry.path();
931        if path.extension().and_then(|extension| extension.to_str()) != Some("json") {
932            continue;
933        }
934        verdict_file_count += 1;
935        if verdict_file_count > EXTERNAL_SIF_RECORDED_VERDICT_SCAN_LIMIT {
936            return true;
937        }
938        let Ok(metadata) = entry.metadata() else {
939            return true;
940        };
941        if !metadata.is_file() || metadata.len() > 1024 * 1024 {
942            return true;
943        }
944        let Ok(source) = fs::read_to_string(path) else {
945            return true;
946        };
947        let Ok(verdict) = read_omena_sif_shard_recorded_verdict_json_v1(source.as_str()) else {
948            return true;
949        };
950        if verdict.canonical_url == canonical_url {
951            return true;
952        }
953    }
954    false
955}
956
957fn verify_recorded_shard_verdict(
958    recorded_verdict_dir: Option<&Path>,
959    verdict: &OmenaSifShardRecordedVerdictV1,
960) -> Result<(), OmenaBridgeExternalSifShardRefusalV1> {
961    let verdict_dir = recorded_verdict_dir
962        .ok_or(OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictSignatureVerificationFailed)?;
963    let reference = verdict.signature.reference.as_str();
964    let relative_bundle_name = reference
965        .strip_prefix(&format!("{EXTERNAL_SIF_RECORDED_BUNDLE_DIR_V1}/"))
966        .and_then(|name| name.strip_suffix(EXTERNAL_SIF_RECORDED_BUNDLE_SUFFIX_V1))
967        .filter(|digest| {
968            digest.len() == 64
969                && digest
970                    .bytes()
971                    .all(|byte| byte.is_ascii_hexdigit() && !byte.is_ascii_uppercase())
972        })
973        .ok_or(OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictSignatureVerificationFailed)?;
974    let bundle_path = verdict_dir
975        .join(EXTERNAL_SIF_RECORDED_BUNDLE_DIR_V1)
976        .join(format!(
977            "{relative_bundle_name}{EXTERNAL_SIF_RECORDED_BUNDLE_SUFFIX_V1}"
978        ));
979    let metadata = fs::metadata(bundle_path.as_path()).map_err(|_| {
980        OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictSignatureVerificationFailed
981    })?;
982    if !metadata.is_file() || metadata.len() > EXTERNAL_SIF_RECORDED_BUNDLE_MAX_BYTES {
983        return Err(
984            OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictSignatureVerificationFailed,
985        );
986    }
987    let bundle_bytes = fs::read(bundle_path.as_path()).map_err(|_| {
988        OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictSignatureVerificationFailed
989    })?;
990    let attested_subject = OmenaSifPublishedAttestationSubjectV1 {
991        schema_version: OMENA_SIF_PUBLISHED_ATTESTATION_SUBJECT_SCHEMA_VERSION_V1.to_string(),
992        product: OMENA_SIF_PUBLISHED_ATTESTATION_SUBJECT_PRODUCT_V1.to_string(),
993        canonical_url: verdict.canonical_url.clone(),
994        trust_tier: verdict.trust_tier,
995        sif_hash: verdict.sif_hash.clone(),
996    };
997    validate_omena_sif_published_attestation_subject_v1(&attested_subject).map_err(|_| {
998        OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictSignatureVerificationFailed
999    })?;
1000    let attested_subject_json =
1001        write_omena_sif_published_attestation_subject_json_v1(&attested_subject).map_err(|_| {
1002            OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictSignatureVerificationFailed
1003        })?;
1004    verify_omena_external_sif_keyless_bundle(
1005        attested_subject_json.as_bytes(),
1006        bundle_bytes.as_slice(),
1007        relative_bundle_name,
1008    )
1009    .map_err(|_| OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictSignatureVerificationFailed)
1010}
1011
1012fn write_external_sif_cache_shard_atomically(
1013    dir: &Path,
1014    key: &str,
1015    bytes: &[u8],
1016) -> std::io::Result<()> {
1017    fs::create_dir_all(dir)?;
1018    crate::cache_root::ensure_omena_cache_root_markers(dir);
1019    let final_path = external_sif_cache_shard_file_path(dir, key).ok_or_else(|| {
1020        std::io::Error::new(
1021            std::io::ErrorKind::InvalidInput,
1022            "invalid external SIF cache key",
1023        )
1024    })?;
1025    let temporary_path = final_path.with_extension(format!("tmp-{}", std::process::id()));
1026    fs::write(temporary_path.as_path(), bytes)?;
1027    let renamed = fs::rename(temporary_path.as_path(), final_path.as_path());
1028    if renamed.is_err() {
1029        let _ = fs::remove_file(temporary_path.as_path());
1030        if final_path.is_file() {
1031            return Ok(());
1032        }
1033    }
1034    renamed
1035}
1036
1037fn enforce_external_sif_cache_caps(dir: &Path) {
1038    let Ok(entries) = fs::read_dir(dir) else {
1039        return;
1040    };
1041    let mut shards = entries
1042        .flatten()
1043        .filter_map(|entry| {
1044            let path = entry.path();
1045            if path.extension().and_then(|extension| extension.to_str()) != Some("json") {
1046                return None;
1047            }
1048            let metadata = entry.metadata().ok()?;
1049            if !metadata.is_file() {
1050                return None;
1051            }
1052            let modified = metadata.modified().ok()?;
1053            Some((modified, metadata.len(), path))
1054        })
1055        .collect::<Vec<_>>();
1056    shards.sort_by(|left, right| left.0.cmp(&right.0).then_with(|| left.2.cmp(&right.2)));
1057    let mut total_bytes = shards.iter().map(|(_, bytes, _)| *bytes).sum::<u64>();
1058    let mut shard_count = shards.len();
1059    for (_, bytes, path) in shards {
1060        if shard_count <= 1
1061            || (shard_count <= EXTERNAL_SIF_CACHE_MAX_SHARDS
1062                && total_bytes <= EXTERNAL_SIF_CACHE_MAX_TOTAL_BYTES)
1063        {
1064            break;
1065        }
1066        if fs::remove_file(path.as_path()).is_ok() {
1067            shard_count -= 1;
1068            total_bytes = total_bytes.saturating_sub(bytes);
1069        }
1070    }
1071}
1072
1073fn external_sif_cache_kill_switch_engaged() -> bool {
1074    std::env::var(EXTERNAL_SIF_CACHE_ENV_KILL_SWITCH)
1075        .is_ok_and(|value| value.eq_ignore_ascii_case("off") || value == "0" || value == "false")
1076}
1077
1078#[cfg(test)]
1079fn clear_external_sif_memory_cache_for_storage_for_test(storage: &OmenaBridgeExternalSifStorageV0) {
1080    let namespace = format!(
1081        "{}\0",
1082        storage
1083            .workspace_cache_root()
1084            .join(EXTERNAL_SIF_CACHE_DIR)
1085            .to_string_lossy()
1086    );
1087    if let Some(cache) = EXTERNAL_SIF_MEMORY_CACHE.get()
1088        && let Ok(mut cache) = cache.lock()
1089    {
1090        cache.retain(|key, _| !key.starts_with(namespace.as_str()));
1091    }
1092}
1093
1094#[cfg(test)]
1095fn local_external_sif_regeneration_count_for_test(canonical_url: &str) -> usize {
1096    EXTERNAL_SIF_LOCAL_REGENERATION_COUNTS
1097        .get_or_init(|| Mutex::new(BTreeMap::new()))
1098        .lock()
1099        .ok()
1100        .and_then(|counts| counts.get(canonical_url).copied())
1101        .unwrap_or_default()
1102}
1103
1104fn infer_omena_bridge_sif_source_syntax(path: &Path) -> OmenaSifSourceSyntaxV1 {
1105    match path
1106        .extension()
1107        .and_then(|extension| extension.to_str())
1108        .map(str::to_ascii_lowercase)
1109        .as_deref()
1110    {
1111        Some("css") => OmenaSifSourceSyntaxV1::Css,
1112        Some("sass") => OmenaSifSourceSyntaxV1::Sass,
1113        Some("less") => OmenaSifSourceSyntaxV1::Less,
1114        _ => OmenaSifSourceSyntaxV1::Scss,
1115    }
1116}
1117
1118pub fn load_omena_bridge_workspace_style_resolution_inputs(
1119    workspace_folder_uri: Option<&str>,
1120    configured_package_manifests: &[OmenaResolverStylePackageManifestV0],
1121) -> OmenaBridgeStyleResolutionInputsV0 {
1122    let workspace_path = workspace_folder_uri
1123        .and_then(file_uri_to_path)
1124        .map(normalize_path);
1125    load_omena_bridge_workspace_style_resolution_inputs_from_path(
1126        workspace_path.as_deref(),
1127        configured_package_manifests,
1128    )
1129}
1130
1131fn load_omena_bridge_workspace_style_resolution_inputs_from_path(
1132    workspace_path: Option<&Path>,
1133    configured_package_manifests: &[OmenaResolverStylePackageManifestV0],
1134) -> OmenaBridgeStyleResolutionInputsV0 {
1135    OmenaBridgeStyleResolutionInputsV0 {
1136        package_manifests: merge_package_manifest_lists(
1137            configured_package_manifests,
1138            workspace_package_manifests(workspace_path).as_slice(),
1139        ),
1140        tsconfig_path_mappings: tsconfig_path_mappings_for_workspace(workspace_path)
1141            .unwrap_or_default(),
1142        bundler_path_mappings: load_omena_bridge_workspace_bundler_path_alias_mappings(
1143            workspace_path,
1144        ),
1145        disk_style_path_identities: workspace_path
1146            .map(workspace_style_path_identities)
1147            .unwrap_or_default(),
1148    }
1149}
1150
1151fn workspace_style_path_identities(
1152    workspace_path: &Path,
1153) -> Vec<OmenaResolverStyleModuleDiskCandidateIdentityV0> {
1154    let mut identities = Vec::new();
1155    let mut queue = VecDeque::from([workspace_path.to_path_buf()]);
1156    while let Some(dir) = queue.pop_front() {
1157        if identities.len() >= WORKSPACE_STYLE_PATH_IDENTITY_SCAN_LIMIT {
1158            break;
1159        }
1160        let Ok(entries) = fs::read_dir(dir.as_path()) else {
1161            continue;
1162        };
1163        for entry in entries.flatten() {
1164            if identities.len() >= WORKSPACE_STYLE_PATH_IDENTITY_SCAN_LIMIT {
1165                break;
1166            }
1167            let path = entry.path();
1168            let Some(file_name) = path.file_name().and_then(|name| name.to_str()) else {
1169                continue;
1170            };
1171            if path.is_dir() {
1172                let relative_depth = path
1173                    .strip_prefix(workspace_path)
1174                    .ok()
1175                    .map(|relative| relative.components().count())
1176                    .unwrap_or(usize::MAX);
1177                if relative_depth > WORKSPACE_STYLE_PATH_IDENTITY_MAX_DEPTH {
1178                    continue;
1179                }
1180                if should_skip_style_identity_scan_dir(file_name) {
1181                    continue;
1182                }
1183                queue.push_back(path);
1184                continue;
1185            }
1186            if !is_indexable_style_path(path.as_path()) {
1187                continue;
1188            }
1189            let Some(metadata_identity) = file_metadata_identity(path.as_path()) else {
1190                continue;
1191            };
1192            identities.push(OmenaResolverStyleModuleDiskCandidateIdentityV0 {
1193                style_path: normalize_path(path).to_string_lossy().to_string(),
1194                metadata_identity,
1195            });
1196        }
1197    }
1198    identities.sort_by(|left, right| left.style_path.cmp(&right.style_path));
1199    identities.dedup_by(|left, right| left.style_path == right.style_path);
1200    identities
1201}
1202
1203fn should_skip_style_identity_scan_dir(name: &str) -> bool {
1204    matches!(
1205        name,
1206        ".git" | ".next" | ".nuxt" | ".svelte-kit" | "coverage" | "target"
1207    )
1208}
1209
1210fn file_metadata_identity(path: &Path) -> Option<String> {
1211    let metadata = fs::symlink_metadata(path).ok()?;
1212    let modified = metadata
1213        .modified()
1214        .ok()
1215        .and_then(|time| time.duration_since(std::time::UNIX_EPOCH).ok())
1216        .map(|duration| format!("{}.{:09}", duration.as_secs(), duration.subsec_nanos()))
1217        .unwrap_or_else(|| "unknownMtime".to_string());
1218    let file_type = if metadata.file_type().is_symlink() {
1219        "symlink"
1220    } else if metadata.is_file() {
1221        "file"
1222    } else {
1223        "other"
1224    };
1225    Some(format!("{file_type}|len{}|mtime{modified}", metadata.len()))
1226}
1227
1228fn merge_package_manifest_lists(
1229    primary: &[OmenaResolverStylePackageManifestV0],
1230    secondary: &[OmenaResolverStylePackageManifestV0],
1231) -> Vec<OmenaResolverStylePackageManifestV0> {
1232    let mut manifests = primary.to_vec();
1233    let mut seen = manifests
1234        .iter()
1235        .map(|manifest| manifest.package_json_path.clone())
1236        .collect::<BTreeSet<_>>();
1237    for manifest in secondary {
1238        if seen.insert(manifest.package_json_path.clone()) {
1239            manifests.push(manifest.clone());
1240        }
1241    }
1242    manifests
1243}
1244
1245fn merged_package_manifests_for_specifier(
1246    source_dir: Option<&Path>,
1247    specifier: &str,
1248    configured_package_manifests: &[OmenaResolverStylePackageManifestV0],
1249) -> Vec<OmenaResolverStylePackageManifestV0> {
1250    merge_package_manifest_lists(
1251        configured_package_manifests,
1252        package_manifests_for_specifier(source_dir, specifier)
1253            .unwrap_or_default()
1254            .as_slice(),
1255    )
1256}
1257
1258fn merged_package_manifests_for_request(
1259    source_dir: Option<&Path>,
1260    workspace_path: Option<&Path>,
1261    specifier: &str,
1262    configured_package_manifests: &[OmenaResolverStylePackageManifestV0],
1263) -> Vec<OmenaResolverStylePackageManifestV0> {
1264    let source_manifests =
1265        merged_package_manifests_for_specifier(source_dir, specifier, configured_package_manifests);
1266    merge_package_manifest_lists(
1267        source_manifests.as_slice(),
1268        workspace_package_manifests(workspace_path).as_slice(),
1269    )
1270}
1271
1272fn tsconfig_path_mappings_for_workspace(
1273    workspace_path: Option<&Path>,
1274) -> Option<Vec<OmenaResolverTsconfigPathMappingV0>> {
1275    let workspace_path = workspace_path?;
1276    let mut mappings = Vec::new();
1277    for config_path in [
1278        workspace_path.join("tsconfig.json"),
1279        workspace_path.join("jsconfig.json"),
1280    ] {
1281        mappings.extend(tsconfig_path_mappings_for_config(config_path.as_path()));
1282    }
1283    Some(mappings)
1284}
1285
1286fn tsconfig_path_mappings_for_config(
1287    config_path: &Path,
1288) -> Vec<OmenaResolverTsconfigPathMappingV0> {
1289    tsconfig_path_mappings_for_config_with_seen(config_path, &mut BTreeSet::new())
1290}
1291
1292fn tsconfig_path_mappings_for_config_with_seen(
1293    config_path: &Path,
1294    seen: &mut BTreeSet<PathBuf>,
1295) -> Vec<OmenaResolverTsconfigPathMappingV0> {
1296    let normalized_config_path = normalize_path(config_path.to_path_buf());
1297    if !seen.insert(normalized_config_path.clone()) {
1298        return Vec::new();
1299    }
1300    let Some(config_text) = fs::read_to_string(config_path).ok() else {
1301        return Vec::new();
1302    };
1303    let Some(config) = serde_json::from_str::<Value>(config_text.as_str()).ok() else {
1304        return Vec::new();
1305    };
1306    let own_mappings = tsconfig_path_mappings_from_value(config_path, &config).unwrap_or_default();
1307    if !own_mappings.is_empty() {
1308        return own_mappings;
1309    }
1310    resolve_tsconfig_extends_path(config_path, &config)
1311        .map(|extends_path| {
1312            tsconfig_path_mappings_for_config_with_seen(extends_path.as_path(), seen)
1313        })
1314        .unwrap_or_default()
1315}
1316
1317fn tsconfig_path_mappings_from_value(
1318    config_path: &Path,
1319    config: &Value,
1320) -> Option<Vec<OmenaResolverTsconfigPathMappingV0>> {
1321    let compiler_options = config.get("compilerOptions")?;
1322    let paths = compiler_options.get("paths")?.as_object()?;
1323    let config_dir = config_path.parent()?;
1324    let base_url = compiler_options
1325        .get("baseUrl")
1326        .and_then(Value::as_str)
1327        .unwrap_or(".");
1328    let base_path = normalize_path(config_dir.join(base_url));
1329    let mut mappings = Vec::new();
1330    for (pattern, targets) in paths {
1331        let Some(targets) = targets.as_array() else {
1332            continue;
1333        };
1334        let target_patterns = targets
1335            .iter()
1336            .filter_map(Value::as_str)
1337            .map(ToString::to_string)
1338            .collect::<Vec<_>>();
1339        if target_patterns.is_empty() {
1340            continue;
1341        }
1342        mappings.push(OmenaResolverTsconfigPathMappingV0 {
1343            base_path: base_path.to_string_lossy().to_string(),
1344            pattern: pattern.to_string(),
1345            target_patterns,
1346        });
1347    }
1348    Some(mappings)
1349}
1350
1351fn resolve_tsconfig_extends_path(config_path: &Path, config: &Value) -> Option<PathBuf> {
1352    let extends = config.get("extends")?.as_str()?;
1353    if !extends.starts_with('.') {
1354        return None;
1355    }
1356    let config_dir = config_path.parent()?;
1357    let raw_path = config_dir.join(extends);
1358    tsconfig_extends_candidates(raw_path)
1359        .into_iter()
1360        .find(|candidate| candidate.exists())
1361}
1362
1363fn tsconfig_extends_candidates(path: PathBuf) -> Vec<PathBuf> {
1364    if path.extension().is_some() {
1365        return vec![path];
1366    }
1367    vec![path.with_extension("json"), path.join("tsconfig.json")]
1368}
1369
1370fn package_manifests_for_specifier(
1371    source_dir: Option<&Path>,
1372    specifier: &str,
1373) -> Option<Vec<OmenaResolverStylePackageManifestV0>> {
1374    if is_package_import_specifier(specifier) {
1375        return Some(package_scope_manifests_for_source_dir(source_dir));
1376    }
1377    let package_name = package_name_from_specifier(specifier)?;
1378    let mut manifests = Vec::new();
1379    let mut seen = BTreeSet::new();
1380    let mut current_dir = source_dir;
1381    while let Some(dir) = current_dir {
1382        let package_json_path = dir
1383            .join("node_modules")
1384            .join(package_name)
1385            .join("package.json");
1386        if seen.insert(package_json_path.clone())
1387            && let Ok(package_json_source) = fs::read_to_string(package_json_path.as_path())
1388        {
1389            manifests.push(OmenaResolverStylePackageManifestV0 {
1390                package_json_path: normalize_path(package_json_path)
1391                    .to_string_lossy()
1392                    .to_string(),
1393                package_json_source,
1394            });
1395        }
1396        current_dir = dir.parent();
1397    }
1398    Some(manifests)
1399}
1400
1401fn package_scope_manifests_for_source_dir(
1402    source_dir: Option<&Path>,
1403) -> Vec<OmenaResolverStylePackageManifestV0> {
1404    let mut manifests = Vec::new();
1405    let mut current_dir = source_dir;
1406    while let Some(dir) = current_dir {
1407        push_workspace_package_manifest(dir.join("package.json"), &mut manifests);
1408        current_dir = dir.parent();
1409    }
1410    manifests
1411}
1412
1413fn workspace_package_manifests(
1414    workspace_path: Option<&Path>,
1415) -> Vec<OmenaResolverStylePackageManifestV0> {
1416    let Some(workspace_path) = workspace_path else {
1417        return Vec::new();
1418    };
1419    let mut manifests = Vec::new();
1420    push_workspace_package_manifest(workspace_path.join("package.json"), &mut manifests);
1421
1422    let node_modules = workspace_path.join("node_modules");
1423    let Ok(entries) = fs::read_dir(node_modules.as_path()) else {
1424        return manifests;
1425    };
1426    for entry in entries.flatten() {
1427        if manifests.len() >= WORKSPACE_PACKAGE_MANIFEST_SCAN_LIMIT {
1428            break;
1429        }
1430        let path = entry.path();
1431        let Some(file_name) = path.file_name().and_then(|value| value.to_str()) else {
1432            continue;
1433        };
1434        if file_name.starts_with('@') {
1435            push_scoped_workspace_package_manifests(path.as_path(), &mut manifests);
1436        } else {
1437            push_workspace_package_manifest(path.join("package.json"), &mut manifests);
1438        }
1439    }
1440    manifests.sort_by(|left, right| left.package_json_path.cmp(&right.package_json_path));
1441    manifests.dedup_by(|left, right| left.package_json_path == right.package_json_path);
1442    manifests
1443}
1444
1445fn push_scoped_workspace_package_manifests(
1446    scope_path: &Path,
1447    manifests: &mut Vec<OmenaResolverStylePackageManifestV0>,
1448) {
1449    let Ok(entries) = fs::read_dir(scope_path) else {
1450        return;
1451    };
1452    for entry in entries.flatten() {
1453        if manifests.len() >= WORKSPACE_PACKAGE_MANIFEST_SCAN_LIMIT {
1454            return;
1455        }
1456        push_workspace_package_manifest(entry.path().join("package.json"), manifests);
1457    }
1458}
1459
1460fn push_workspace_package_manifest(
1461    package_json_path: PathBuf,
1462    manifests: &mut Vec<OmenaResolverStylePackageManifestV0>,
1463) {
1464    if manifests.len() >= WORKSPACE_PACKAGE_MANIFEST_SCAN_LIMIT {
1465        return;
1466    }
1467    let normalized_package_json_path = normalize_path(package_json_path);
1468    let package_json_path_text = normalized_package_json_path.to_string_lossy().to_string();
1469    if manifests
1470        .iter()
1471        .any(|manifest| manifest.package_json_path == package_json_path_text)
1472    {
1473        return;
1474    }
1475    let Ok(package_json_source) = fs::read_to_string(normalized_package_json_path.as_path()) else {
1476        return;
1477    };
1478    manifests.push(OmenaResolverStylePackageManifestV0 {
1479        package_json_path: package_json_path_text,
1480        package_json_source,
1481    });
1482}
1483
1484fn package_name_from_specifier(specifier: &str) -> Option<&str> {
1485    let specifier = specifier.strip_prefix("pkg:").unwrap_or(specifier);
1486    if specifier.starts_with('.')
1487        || specifier.starts_with('/')
1488        || is_package_import_specifier(specifier)
1489        || is_external_style_specifier(specifier)
1490    {
1491        return None;
1492    }
1493    if specifier.starts_with('@') {
1494        let mut segments = specifier.splitn(3, '/');
1495        let scope = segments.next()?;
1496        let package = segments.next()?;
1497        if scope.len() <= 1 || package.is_empty() {
1498            return None;
1499        }
1500        return specifier.get(..scope.len() + 1 + package.len());
1501    }
1502    specifier.split('/').next().filter(|name| !name.is_empty())
1503}
1504
1505fn is_package_import_specifier(specifier: &str) -> bool {
1506    specifier
1507        .strip_prefix("pkg:")
1508        .unwrap_or(specifier)
1509        .starts_with('#')
1510}
1511
1512fn tsconfig_path_pattern_matches(pattern: &str, specifier: &str) -> bool {
1513    if let Some((prefix, suffix)) = pattern.split_once('*') {
1514        return !suffix.contains('*')
1515            && specifier.starts_with(prefix)
1516            && specifier.ends_with(suffix)
1517            && specifier.len() >= prefix.len() + suffix.len();
1518    }
1519    pattern == specifier
1520}
1521
1522fn bundler_path_alias_pattern_matches(pattern: &str, specifier: &str) -> bool {
1523    if pattern.is_empty() {
1524        return false;
1525    }
1526    if let Some(exact_pattern) = pattern.strip_suffix('$') {
1527        return specifier == exact_pattern;
1528    }
1529    if pattern == specifier {
1530        return true;
1531    }
1532    let prefix = if pattern.ends_with('/') {
1533        pattern.to_string()
1534    } else {
1535        format!("{pattern}/")
1536    };
1537    specifier.starts_with(prefix.as_str())
1538}
1539
1540fn is_external_style_specifier(specifier: &str) -> bool {
1541    specifier.starts_with("sass:")
1542        || specifier.starts_with("http://")
1543        || specifier.starts_with("https://")
1544}
1545
1546fn style_uri_for_resolver_candidates(
1547    candidates: &[String],
1548    disk_style_path_identities: &[OmenaResolverStyleModuleDiskCandidateIdentityV0],
1549    requires_existing_candidate: bool,
1550) -> Option<String> {
1551    let empty_available = BTreeSet::new();
1552    let confirmation = confirm_omena_resolver_style_module_candidate_with_options(
1553        candidates,
1554        &empty_available,
1555        disk_style_path_identities,
1556        OmenaResolverStyleModuleConfirmationOptionsV0 {
1557            allow_disk_confirmation: true,
1558            allow_live_disk_confirmation: true,
1559            allow_unconfirmed_indexable_candidate: !requires_existing_candidate,
1560            ..OmenaResolverStyleModuleConfirmationOptionsV0::default()
1561        },
1562    );
1563    confirmation
1564        .resolved_style_path
1565        .map(PathBuf::from)
1566        .map(|path| path_to_file_uri(normalize_path(path).as_path()))
1567}
1568
1569fn is_indexable_style_path(path: &Path) -> bool {
1570    is_omena_resolver_indexable_style_module_path(path.to_string_lossy().as_ref())
1571}
1572
1573fn file_uri_to_path(uri: &str) -> Option<PathBuf> {
1574    let raw_path = uri.strip_prefix("file://")?;
1575    Some(PathBuf::from(percent_decode_uri_path(raw_path)?))
1576}
1577
1578fn percent_decode_uri_path(raw_path: &str) -> Option<String> {
1579    let bytes = raw_path.as_bytes();
1580    let mut decoded = Vec::with_capacity(bytes.len());
1581    let mut index = 0usize;
1582    while index < bytes.len() {
1583        if bytes[index] == b'%' {
1584            let high = bytes.get(index + 1).and_then(|byte| hex_value(*byte))?;
1585            let low = bytes.get(index + 2).and_then(|byte| hex_value(*byte))?;
1586            decoded.push((high << 4) | low);
1587            index += 3;
1588        } else {
1589            decoded.push(bytes[index]);
1590            index += 1;
1591        }
1592    }
1593    String::from_utf8(decoded).ok()
1594}
1595
1596fn hex_value(byte: u8) -> Option<u8> {
1597    match byte {
1598        b'0'..=b'9' => Some(byte - b'0'),
1599        b'a'..=b'f' => Some(byte - b'a' + 10),
1600        b'A'..=b'F' => Some(byte - b'A' + 10),
1601        _ => None,
1602    }
1603}
1604
1605fn path_to_file_uri(path: &Path) -> String {
1606    let path = normalize_path(path.to_path_buf());
1607    format!(
1608        "file://{}",
1609        percent_encode_uri_path(path.to_string_lossy().as_ref())
1610    )
1611}
1612
1613fn percent_encode_uri_path(path: &str) -> String {
1614    let mut encoded = String::with_capacity(path.len());
1615    for byte in path.as_bytes() {
1616        match *byte {
1617            b'A'..=b'Z'
1618            | b'a'..=b'z'
1619            | b'0'..=b'9'
1620            | b'-'
1621            | b'.'
1622            | b'_'
1623            | b'~'
1624            | b'/'
1625            | b'@'
1626            | b':'
1627            | b'!'
1628            | b'$'
1629            | b'&'
1630            | b'\''
1631            | b'*'
1632            | b'+'
1633            | b','
1634            | b';'
1635            | b'=' => encoded.push(*byte as char),
1636            _ => encoded.push_str(format!("%{byte:02X}").as_str()),
1637        }
1638    }
1639    encoded
1640}
1641
1642fn normalize_path(path: PathBuf) -> PathBuf {
1643    if let Some(canonical) = canonicalize_existing_path_or_parent(path.as_path()) {
1644        return normalize_path_lexical(canonical);
1645    }
1646    normalize_path_lexical(path)
1647}
1648
1649fn canonicalize_existing_path_or_parent(path: &Path) -> Option<PathBuf> {
1650    if let Ok(canonical) = fs::canonicalize(path) {
1651        return Some(canonical);
1652    }
1653
1654    let mut current = path.to_path_buf();
1655    let mut suffix = Vec::<OsString>::new();
1656    while let Some(parent) = current.parent() {
1657        if let Some(file_name) = current.file_name() {
1658            suffix.push(file_name.to_os_string());
1659        }
1660        if let Ok(mut canonical_parent) = fs::canonicalize(parent) {
1661            for segment in suffix.iter().rev() {
1662                canonical_parent.push(segment);
1663            }
1664            return Some(canonical_parent);
1665        }
1666        current = parent.to_path_buf();
1667    }
1668    None
1669}
1670
1671fn normalize_path_lexical(path: PathBuf) -> PathBuf {
1672    let mut normalized = PathBuf::new();
1673    for component in path.components() {
1674        match component {
1675            Component::CurDir => {}
1676            Component::ParentDir => {
1677                normalized.pop();
1678            }
1679            Component::Normal(_) | Component::RootDir | Component::Prefix(_) => {
1680                normalized.push(component.as_os_str());
1681            }
1682        }
1683    }
1684    normalized
1685}
1686
1687#[cfg(test)]
1688mod tests {
1689    use std::{fs, time::SystemTime};
1690
1691    use super::*;
1692
1693    #[test]
1694    fn resolves_relative_style_candidates() -> Result<(), Box<dyn std::error::Error>> {
1695        let root = temp_dir("omena_bridge_style_relative")?;
1696        let source = root.join("src/App.tsx");
1697        let style = root.join("src/Button.module.scss");
1698        fs::create_dir_all(
1699            source
1700                .parent()
1701                .ok_or_else(|| std::io::Error::other("parent"))?,
1702        )?;
1703        fs::write(&source, "")?;
1704        fs::write(&style, ".root {}")?;
1705
1706        let uri = resolve_omena_bridge_style_uri_for_specifier(
1707            path_to_file_uri(source.as_path()).as_str(),
1708            Some(path_to_file_uri(root.as_path()).as_str()),
1709            "./Button.module.scss",
1710        );
1711
1712        assert_eq!(
1713            uri.as_deref(),
1714            Some(path_to_file_uri(style.as_path()).as_str())
1715        );
1716        let _ = fs::remove_dir_all(root);
1717        Ok(())
1718    }
1719
1720    #[test]
1721    fn generates_sif_for_resolved_relative_style_module() -> Result<(), Box<dyn std::error::Error>>
1722    {
1723        let root = temp_dir("omena_bridge_sif_resolved")?;
1724        let source = root.join("src/App.tsx");
1725        let style = root.join("src/theme.scss");
1726        fs::create_dir_all(
1727            style
1728                .parent()
1729                .ok_or_else(|| std::io::Error::other("parent"))?,
1730        )?;
1731        fs::write(&source, "")?;
1732        fs::write(&style, "$brand: #0af;\n@mixin focus-ring {}\n")?;
1733
1734        let resolved = resolve_omena_bridge_style_uri_for_specifier(
1735            path_to_file_uri(source.as_path()).as_str(),
1736            Some(path_to_file_uri(root.as_path()).as_str()),
1737            "./theme.scss",
1738        )
1739        .ok_or_else(|| std::io::Error::other("resolution failed"))?;
1740
1741        let sif = generate_fixture_sif_for_resolved_style_path(style.as_path(), resolved.as_str())?;
1742
1743        assert_eq!(sif.canonical_url, resolved);
1744        assert_eq!(sif.source.syntax, OmenaSifSourceSyntaxV1::Scss);
1745        assert!(
1746            sif.exports
1747                .variables
1748                .iter()
1749                .any(|variable| variable.name == "$brand"),
1750            "expected $brand variable export, got {:?}",
1751            sif.exports.variables
1752        );
1753        assert!(
1754            sif.exports
1755                .mixins
1756                .iter()
1757                .any(|mixin| mixin.name == "focus-ring"),
1758            "expected focus-ring mixin export, got {:?}",
1759            sif.exports.mixins
1760        );
1761        // The produced SIF must round-trip through the exact JSON contract the
1762        // CLI's `read_external_sifs` consumes, proving it is a valid artifact.
1763        let json = omena_sif::write_omena_sif_json_v1(&sif)?;
1764        let parsed = omena_sif::read_omena_sif_json_v1(json.as_str())?;
1765        assert_eq!(parsed, sif);
1766        let _ = fs::remove_dir_all(root);
1767        Ok(())
1768    }
1769
1770    #[test]
1771    fn generates_lif_exports_for_resolved_less_specifier() -> Result<(), Box<dyn std::error::Error>>
1772    {
1773        let root = temp_dir("omena_bridge_lif_resolved_less")?;
1774        let source = root.join("src/App.tsx");
1775        let style = root.join("src/tokens.less");
1776        fs::create_dir_all(
1777            style
1778                .parent()
1779                .ok_or_else(|| std::io::Error::other("parent"))?,
1780        )?;
1781        fs::write(&source, "")?;
1782        fs::write(
1783            &style,
1784            "@brand: #fff;\n@tokens: { primary: @brand; @gap: 2px; };\n.button(@gap: 1rem) when (@gap > 0) { color: @brand; }\n",
1785        )?;
1786
1787        let resolved = resolve_omena_bridge_style_uri_for_specifier(
1788            path_to_file_uri(source.as_path()).as_str(),
1789            Some(path_to_file_uri(root.as_path()).as_str()),
1790            "./tokens.less",
1791        )
1792        .ok_or_else(|| std::io::Error::other("Less resolution failed"))?;
1793
1794        let exports = generate_omena_bridge_lif_exports_for_resolved_style_path(resolved.as_str())?;
1795
1796        assert_eq!(exports.less_variables[0].name, "@brand");
1797        assert_eq!(
1798            exports.less_variables[0].value_repr.as_deref(),
1799            Some("#fff")
1800        );
1801        assert_eq!(exports.less_mixins[0].name, ".button");
1802        assert!(exports.less_mixins[0].guarded);
1803        assert_eq!(exports.less_detached_rulesets[0].name, "@tokens");
1804        assert_eq!(
1805            exports.less_detached_rulesets[0].member_names,
1806            vec!["@gap", "primary"]
1807        );
1808        let _ = fs::remove_dir_all(root);
1809        Ok(())
1810    }
1811
1812    #[test]
1813    fn generates_sif_from_plain_resolved_path() -> Result<(), Box<dyn std::error::Error>> {
1814        let root = temp_dir("omena_bridge_sif_plain")?;
1815        let style = root.join("tokens.sass");
1816        fs::write(&style, "$gap: 8px\n")?;
1817
1818        let sif = generate_fixture_sif_for_resolved_style_path(
1819            style.as_path(),
1820            style.to_string_lossy().as_ref(),
1821        )?;
1822
1823        assert_eq!(sif.source.syntax, OmenaSifSourceSyntaxV1::Sass);
1824        let _ = fs::remove_dir_all(root);
1825        Ok(())
1826    }
1827
1828    #[test]
1829    fn generates_sif_with_less_source_syntax() -> Result<(), Box<dyn std::error::Error>> {
1830        let root = temp_dir("omena_bridge_sif_less")?;
1831        let style = root.join("tokens.less");
1832        fs::write(&style, "@gap: 8px;\n.button { margin: @gap; }\n")?;
1833
1834        let sif = generate_fixture_sif_for_resolved_style_path(
1835            style.as_path(),
1836            style.to_string_lossy().as_ref(),
1837        )?;
1838
1839        assert_eq!(sif.source.syntax, OmenaSifSourceSyntaxV1::Less);
1840        let json = omena_sif::write_omena_sif_json_v1(&sif)?;
1841        assert!(json.contains(r#""syntax":"less""#));
1842        let _ = fs::remove_dir_all(root);
1843        Ok(())
1844    }
1845
1846    #[test]
1847    fn generates_lif_exports_for_resolved_less_path() -> Result<(), Box<dyn std::error::Error>> {
1848        let root = temp_dir("omena_bridge_lif_less")?;
1849        let style = root.join("tokens.less");
1850        fs::write(
1851            &style,
1852            "@brand: red;\n@tokens: { primary: @brand; };\n.button(@gap: 1rem) { color: @brand; }\n",
1853        )?;
1854
1855        let exports = generate_omena_bridge_lif_exports_for_resolved_style_path(
1856            style.to_string_lossy().as_ref(),
1857        )?;
1858
1859        assert_eq!(exports.less_variables[0].name, "@brand");
1860        assert_eq!(exports.less_mixins[0].name, ".button");
1861        assert_eq!(exports.less_detached_rulesets[0].name, "@tokens");
1862        assert_eq!(
1863            exports.less_detached_rulesets[0].member_names,
1864            vec!["primary"]
1865        );
1866        let _ = fs::remove_dir_all(root);
1867        Ok(())
1868    }
1869
1870    #[test]
1871    fn external_sif_cache_key_is_base_dir_sensitive_and_serves_fresh_sif()
1872    -> Result<(), Box<dyn std::error::Error>> {
1873        let root = temp_dir("omena_bridge_external_sif_cache")?;
1874        let first_dir = root.join("node_modules/design-a");
1875        let second_dir = root.join("node_modules/design-b");
1876        fs::create_dir_all(first_dir.as_path())?;
1877        fs::create_dir_all(second_dir.as_path())?;
1878        fs::write(root.join("package.json"), r#"{"name":"workspace"}"#)?;
1879        let first_style = first_dir.join("tokens.scss");
1880        let second_style = second_dir.join("tokens.scss");
1881        let source = "$brand: #0af;\n";
1882        fs::write(first_style.as_path(), source)?;
1883        fs::write(second_style.as_path(), source)?;
1884
1885        let first_path = normalize_path(first_style.clone());
1886        let second_path = normalize_path(second_style.clone());
1887        let source_hash = compute_omena_sif_leaf_hash_v1(source.as_bytes())
1888            .as_str()
1889            .to_string();
1890        let first_base_dir = first_path
1891            .parent()
1892            .ok_or_else(|| std::io::Error::other("first parent"))?
1893            .to_string_lossy()
1894            .to_string();
1895        let second_base_dir = second_path
1896            .parent()
1897            .ok_or_else(|| std::io::Error::other("second parent"))?
1898            .to_string_lossy()
1899            .to_string();
1900        let first_key = external_sif_cache_key(
1901            source_hash.as_str(),
1902            first_base_dir.as_str(),
1903            path_to_file_uri(first_path.as_path()).as_str(),
1904            None,
1905        );
1906        let second_key = external_sif_cache_key(
1907            source_hash.as_str(),
1908            second_base_dir.as_str(),
1909            path_to_file_uri(second_path.as_path()).as_str(),
1910            None,
1911        );
1912        assert_ne!(
1913            first_key, second_key,
1914            "same bytes under different resolved bases must not share an external SIF cache key"
1915        );
1916        let old_fingerprint_key = external_sif_cache_key(
1917            source_hash.as_str(),
1918            first_base_dir.as_str(),
1919            path_to_file_uri(first_path.as_path()).as_str(),
1920            Some("lockfile:old"),
1921        );
1922        let new_fingerprint_key = external_sif_cache_key(
1923            source_hash.as_str(),
1924            first_base_dir.as_str(),
1925            path_to_file_uri(first_path.as_path()).as_str(),
1926            Some("lockfile:new"),
1927        );
1928        assert_ne!(
1929            old_fingerprint_key, new_fingerprint_key,
1930            "lockfile or package-manager freshness changes must invalidate external SIF cache keys"
1931        );
1932        let old_crate_version_key = external_sif_cache_key_with_crate_version(
1933            source_hash.as_str(),
1934            first_base_dir.as_str(),
1935            path_to_file_uri(first_path.as_path()).as_str(),
1936            None,
1937            "0.2.0",
1938        );
1939        let current_crate_version_key = external_sif_cache_key_with_crate_version(
1940            source_hash.as_str(),
1941            first_base_dir.as_str(),
1942            path_to_file_uri(first_path.as_path()).as_str(),
1943            None,
1944            env!("CARGO_PKG_VERSION"),
1945        );
1946        assert_ne!(
1947            old_crate_version_key, current_crate_version_key,
1948            "a shard address from another crate version must never be served"
1949        );
1950
1951        let first_uri = path_to_file_uri(first_style.as_path());
1952        let cache_storage = fixture_cache_storage(first_style.as_path());
1953        let fresh =
1954            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
1955                first_uri.as_str(),
1956                &OmenaBridgeExternalSifCacheContextV0::default(),
1957                Some(&cache_storage),
1958            )?;
1959        clear_external_sif_memory_cache_for_storage_for_test(&cache_storage);
1960        let cached =
1961            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
1962                first_uri.as_str(),
1963                &OmenaBridgeExternalSifCacheContextV0::default(),
1964                Some(&cache_storage),
1965            )?;
1966        assert_eq!(cached, fresh);
1967        let cache_dir =
1968            external_sif_cache_dir_for_path(first_style.as_path(), Some(&cache_storage))
1969                .ok_or_else(|| std::io::Error::other("cache dir"))?;
1970        assert!(
1971            cache_dir.read_dir()?.flatten().any(|entry| entry
1972                .path()
1973                .extension()
1974                .and_then(|ext| ext.to_str())
1975                == Some("json")),
1976            "expected a disk external SIF cache shard in {}",
1977            cache_dir.display()
1978        );
1979        let _ = fs::remove_dir_all(root);
1980        Ok(())
1981    }
1982
1983    #[test]
1984    fn memory_cache_hit_uses_fresh_source_hash_without_local_regeneration()
1985    -> Result<(), Box<dyn std::error::Error>> {
1986        let root = temp_dir("omena_bridge_external_sif_memory_fast_path")?;
1987        fs::write(root.join("package.json"), r#"{"name":"workspace"}"#)?;
1988        let style = root.join("tokens.scss");
1989        fs::write(style.as_path(), "$brand: #0af;\n")?;
1990        let resolved = path_to_file_uri(style.as_path());
1991        let storage = fixture_cache_storage(style.as_path());
1992        clear_external_sif_memory_cache_for_storage_for_test(&storage);
1993
1994        let before = local_external_sif_regeneration_count_for_test(resolved.as_str());
1995        let first =
1996            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
1997                resolved.as_str(),
1998                &OmenaBridgeExternalSifCacheContextV0::default(),
1999                Some(&storage),
2000            )?;
2001        let after_first = local_external_sif_regeneration_count_for_test(resolved.as_str());
2002        assert_eq!(after_first, before + 1);
2003
2004        let second =
2005            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
2006                resolved.as_str(),
2007                &OmenaBridgeExternalSifCacheContextV0::default(),
2008                Some(&storage),
2009            )?;
2010        assert_eq!(second, first);
2011        assert_eq!(
2012            local_external_sif_regeneration_count_for_test(resolved.as_str()),
2013            after_first,
2014            "a memory hit bound to the freshly read source hash must skip static SIF regeneration"
2015        );
2016
2017        fs::write(style.as_path(), "$brand: #f50;\n")?;
2018        let changed =
2019            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
2020                resolved.as_str(),
2021                &OmenaBridgeExternalSifCacheContextV0::default(),
2022                Some(&storage),
2023            )?;
2024        assert_ne!(changed, first);
2025        assert_eq!(
2026            local_external_sif_regeneration_count_for_test(resolved.as_str()),
2027            after_first + 1,
2028            "changed source bytes must miss the source-hash-addressed memory entry"
2029        );
2030
2031        let _ = fs::remove_dir_all(root);
2032        Ok(())
2033    }
2034
2035    #[test]
2036    #[ignore = "release-only external SIF cache timing probe"]
2037    fn release_external_sif_source_hash_cache_probe() -> Result<(), Box<dyn std::error::Error>> {
2038        assert!(
2039            !std::hint::black_box(cfg!(debug_assertions)),
2040            "run this ignored probe with cargo test --release"
2041        );
2042        let root = temp_dir("omena_bridge_external_sif_release_probe")?;
2043        fs::write(root.join("package.json"), r#"{"name":"workspace"}"#)?;
2044        let style = root.join("tokens.scss");
2045        let source = (0..400)
2046            .map(|index| format!("$token-{index}: #{:06x};\n", index * 97))
2047            .collect::<String>();
2048        fs::write(style.as_path(), source.as_bytes())?;
2049        let resolved = path_to_file_uri(style.as_path());
2050        let storage = fixture_cache_storage(style.as_path());
2051        clear_external_sif_memory_cache_for_storage_for_test(&storage);
2052        let cache_context = OmenaBridgeExternalSifCacheContextV0::default();
2053        let _ = generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
2054            resolved.as_str(),
2055            &cache_context,
2056            Some(&storage),
2057        )?;
2058
2059        const ITERATIONS: usize = 200;
2060        let cached_start = std::time::Instant::now();
2061        for _ in 0..ITERATIONS {
2062            std::hint::black_box(
2063                generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
2064                    resolved.as_str(),
2065                    &cache_context,
2066                    Some(&storage),
2067                )?,
2068            );
2069        }
2070        let cached_elapsed = cached_start.elapsed();
2071
2072        let regenerated_start = std::time::Instant::now();
2073        for _ in 0..ITERATIONS {
2074            let bytes = fs::read(style.as_path())?;
2075            std::hint::black_box(compute_omena_sif_leaf_hash_v1(bytes.as_slice()));
2076            let text = String::from_utf8(bytes)?;
2077            std::hint::black_box(generate_static_omena_sif_v1(
2078                OmenaSifStaticGeneratorInputV1 {
2079                    canonical_url: resolved.as_str(),
2080                    source: text.as_str(),
2081                    syntax: OmenaSifSourceSyntaxV1::Scss,
2082                },
2083            )?);
2084        }
2085        let regenerated_elapsed = regenerated_start.elapsed();
2086        eprintln!(
2087            "externalSifCacheReleaseProbe rules=400 iterations={ITERATIONS} cachedNs={} regeneratedNs={} speedup={:.3}",
2088            cached_elapsed.as_nanos(),
2089            regenerated_elapsed.as_nanos(),
2090            regenerated_elapsed.as_secs_f64() / cached_elapsed.as_secs_f64()
2091        );
2092        assert!(
2093            cached_elapsed < regenerated_elapsed,
2094            "source-hash memory validation must be cheaper than repeated local regeneration"
2095        );
2096
2097        let _ = fs::remove_dir_all(root);
2098        Ok(())
2099    }
2100
2101    #[test]
2102    fn global_external_sif_storage_never_cross_serves_workspace_partitions()
2103    -> Result<(), Box<dyn std::error::Error>> {
2104        let root = temp_dir("omena_bridge_workspace_partitioned_global_sif")?;
2105        let workspace_a = root.join("workspace-a");
2106        let workspace_b = root.join("workspace-b");
2107        fs::create_dir_all(workspace_a.as_path())?;
2108        fs::create_dir_all(workspace_b.as_path())?;
2109        fs::write(
2110            workspace_a.join("package.json"),
2111            r#"{"name":"workspace-a"}"#,
2112        )?;
2113        fs::write(
2114            workspace_b.join("package.json"),
2115            r#"{"name":"workspace-b"}"#,
2116        )?;
2117        let shared_package = root.join("node_modules").join("design-system");
2118        fs::create_dir_all(shared_package.as_path())?;
2119        let style = shared_package.join("tokens.scss");
2120        fs::write(style.as_path(), "$brand: #0af;\n")?;
2121
2122        let platform_cache_home = root.join("global");
2123        let workspace_identity_a = workspace_a.to_string_lossy().into_owned();
2124        let workspace_identity_b = workspace_b.to_string_lossy().into_owned();
2125        let derived_workspace_root = |workspace_root: &Path, workspace_identity: &str| {
2126            crate::cache_root::resolve_omena_cache_roots(
2127                crate::cache_root::CacheRootResolverInputsV0 {
2128                    platform_cache_home: Some(platform_cache_home.as_path()),
2129                    workspace_root: Some(workspace_root),
2130                    workspace_identity: Some(workspace_identity),
2131                    ..crate::cache_root::CacheRootResolverInputsV0::default()
2132                },
2133            )
2134            .workspace
2135        };
2136        let workspace_cache_root_a =
2137            derived_workspace_root(workspace_a.as_path(), workspace_identity_a.as_str())
2138                .ok_or_else(|| std::io::Error::other("workspace A cache root"))?;
2139        let workspace_cache_root_b =
2140            derived_workspace_root(workspace_b.as_path(), workspace_identity_b.as_str())
2141                .ok_or_else(|| std::io::Error::other("workspace B cache root"))?;
2142        assert_ne!(workspace_cache_root_a, workspace_cache_root_b);
2143        let storage_a = OmenaBridgeExternalSifStorageV0::from_workspace_cache_root_and_identity(
2144            workspace_cache_root_a,
2145            workspace_identity_a,
2146        );
2147        let storage_b = OmenaBridgeExternalSifStorageV0::from_workspace_cache_root_and_identity(
2148            workspace_cache_root_b,
2149            workspace_identity_b,
2150        );
2151        let cache_context = OmenaBridgeExternalSifCacheContextV0::default();
2152        clear_external_sif_memory_cache_for_storage_for_test(&storage_a);
2153        clear_external_sif_memory_cache_for_storage_for_test(&storage_b);
2154        let sif_a =
2155            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
2156                style.to_string_lossy().as_ref(),
2157                &cache_context,
2158                Some(&storage_a),
2159            )?;
2160        let sif_b =
2161            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
2162                style.to_string_lossy().as_ref(),
2163                &cache_context,
2164                Some(&storage_b),
2165            )?;
2166        assert_eq!(sif_a, sif_b);
2167
2168        let cache_dir_a = storage_a
2169            .workspace_cache_root()
2170            .join(EXTERNAL_SIF_CACHE_DIR);
2171        let cache_dir_b = storage_b
2172            .workspace_cache_root()
2173            .join(EXTERNAL_SIF_CACHE_DIR);
2174        let shard_files = |dir: &Path| -> Vec<PathBuf> {
2175            let Ok(entries) = fs::read_dir(dir) else {
2176                return Vec::new();
2177            };
2178            let mut files = entries
2179                .flatten()
2180                .map(|entry| entry.path())
2181                .filter(|path| {
2182                    path.extension()
2183                        .is_some_and(|extension| extension == "json")
2184                })
2185                .collect::<Vec<_>>();
2186            files.sort();
2187            files
2188        };
2189        let shard_a = shard_files(cache_dir_a.as_path());
2190        let shard_b = shard_files(cache_dir_b.as_path());
2191        assert_eq!(shard_a.len(), 1);
2192        assert_eq!(shard_b.len(), 1);
2193        assert_ne!(shard_a, shard_b);
2194        let shard_a_bytes = fs::read(shard_a[0].as_path())?;
2195        let mut poisoned_shard = serde_json::from_slice::<Value>(shard_a_bytes.as_slice())?;
2196        let mut poisoned_sif = sif_a.clone();
2197        poisoned_sif.exports.variables.clear();
2198        let poisoned_sif_json = write_omena_sif_json_v1(&poisoned_sif)?;
2199        poisoned_shard["sifJson"] = Value::String(poisoned_sif_json.clone());
2200        poisoned_shard["payloadDigest"] = Value::String(
2201            compute_omena_sif_leaf_hash_v1(poisoned_sif_json.as_bytes())
2202                .as_str()
2203                .to_string(),
2204        );
2205        fs::write(
2206            shard_a[0].as_path(),
2207            write_omena_canonical_json_bytes_v1(&poisoned_shard)?,
2208        )?;
2209        clear_external_sif_memory_cache_for_storage_for_test(&storage_a);
2210        let poisoned_a =
2211            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
2212                style.to_string_lossy().as_ref(),
2213                &cache_context,
2214                Some(&storage_a),
2215            )?;
2216        assert_eq!(poisoned_a, sif_a);
2217        assert_ne!(poisoned_a, poisoned_sif);
2218        let isolated_b =
2219            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
2220                style.to_string_lossy().as_ref(),
2221                &cache_context,
2222                Some(&storage_b),
2223            )?;
2224        assert_eq!(isolated_b, sif_b);
2225        assert_eq!(isolated_b, poisoned_a);
2226        assert_ne!(isolated_b, poisoned_sif);
2227        assert_eq!(shard_files(cache_dir_a.as_path()).len(), 1);
2228        assert_eq!(shard_files(cache_dir_b.as_path()).len(), 1);
2229        eprintln!(
2230            "externalSifStorage globalBase={} workspaceA={} workspaceB={} shardsA=1 shardsB=1 crossWorkspaceServe=false",
2231            platform_cache_home
2232                .join("omena")
2233                .join("workspaces")
2234                .display(),
2235            cache_dir_a.display(),
2236            cache_dir_b.display(),
2237        );
2238
2239        let _ = fs::remove_dir_all(root);
2240        Ok(())
2241    }
2242
2243    #[test]
2244    fn cache_payload_without_recorded_verdict_is_replaced_by_local_regeneration()
2245    -> Result<(), Box<dyn std::error::Error>> {
2246        let fixture = recorded_verdict_attack_fixture("cache-payload-without-verdict")?;
2247        write_poisoned_low_tier_fixture_shard(&fixture)?;
2248        clear_external_sif_memory_cache_for_storage_for_test(&fixture.storage);
2249
2250        let loaded =
2251            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_storage_and_trust(
2252                fixture.resolved.as_str(),
2253                &OmenaBridgeExternalSifCacheContextV0::default(),
2254                Some(&fixture.storage),
2255            )?;
2256
2257        assert_eq!(loaded.sif, fixture.original_sif);
2258        assert_ne!(loaded.sif, fixture.poisoned_sif);
2259        fs::remove_dir_all(fixture.root)?;
2260        Ok(())
2261    }
2262
2263    #[test]
2264    fn deleting_recorded_verdict_does_not_enable_cached_payload_substitution()
2265    -> Result<(), Box<dyn std::error::Error>> {
2266        let fixture = recorded_verdict_attack_fixture("deleted-verdict-cache-payload")?;
2267        write_fixture_recorded_shard_verdict(
2268            &fixture.storage,
2269            &fixture.original_sif,
2270            OmenaSifTrustTierV1::T2,
2271        )?;
2272        fs::remove_dir_all(fixture.verdict_dir.as_path())?;
2273        write_poisoned_low_tier_fixture_shard(&fixture)?;
2274        clear_external_sif_memory_cache_for_storage_for_test(&fixture.storage);
2275
2276        let loaded =
2277            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_storage_and_trust(
2278                fixture.resolved.as_str(),
2279                &OmenaBridgeExternalSifCacheContextV0::default(),
2280                Some(&fixture.storage),
2281            )?;
2282
2283        assert_eq!(loaded.sif, fixture.original_sif);
2284        assert_ne!(loaded.sif, fixture.poisoned_sif);
2285        fs::remove_dir_all(fixture.root)?;
2286        Ok(())
2287    }
2288
2289    #[test]
2290    fn parsed_sif_canonical_url_must_match_requested_resource()
2291    -> Result<(), Box<dyn std::error::Error>> {
2292        let fixture = recorded_verdict_attack_fixture("canonical-url-confused-deputy")?;
2293        let mut substituted_sif = fixture.poisoned_sif.clone();
2294        substituted_sif.canonical_url = "pkg:untrusted/substituted.scss".to_string();
2295        let substituted_sif_json = write_omena_sif_json_v1(&substituted_sif)?;
2296        let substituted_payload_digest =
2297            compute_omena_sif_leaf_hash_v1(substituted_sif_json.as_bytes());
2298        let substituted_sif_hash = compute_omena_sif_artifact_hash_v1(&substituted_sif)?;
2299        let mut shard = fixture.original_shard.clone();
2300        shard["sifJson"] = Value::String(substituted_sif_json);
2301        shard["payloadDigest"] = Value::String(substituted_payload_digest.as_str().to_string());
2302        shard["trustEnvelope"] = serde_json::to_value(OmenaSifShardTrustEnvelopeV1 {
2303            schema_version: OMENA_SIF_SHARD_TRUST_ENVELOPE_SCHEMA_VERSION_V1.to_string(),
2304            product: OMENA_SIF_SHARD_TRUST_ENVELOPE_PRODUCT_V1.to_string(),
2305            trust_tier: OmenaSifTrustTierV1::T1,
2306            payload_digest: substituted_payload_digest,
2307            signature: None,
2308            lock_binding: OmenaSifShardLockBindingV1 {
2309                canonical_url: fixture.resolved.clone(),
2310                sif_hash: substituted_sif_hash,
2311            },
2312        })?;
2313
2314        let loaded = validate_external_sif_cache_shard(
2315            &shard,
2316            fixture.key.as_str(),
2317            fixture.resolved.as_str(),
2318            fixture.source_hash.as_str(),
2319            fixture.resolved_base_dir.as_str(),
2320            None,
2321            &fixture.original_sif,
2322        );
2323        assert_eq!(
2324            loaded,
2325            Err(OmenaBridgeExternalSifShardRefusalV1::CanonicalUrlMismatch),
2326            "a parsed SIF for another canonical URL must reach the named confused-deputy refusal"
2327        );
2328        fs::remove_dir_all(fixture.root)?;
2329        Ok(())
2330    }
2331
2332    #[test]
2333    fn unverified_recorded_verdict_never_elevates_memory_or_disk_shards()
2334    -> Result<(), Box<dyn std::error::Error>> {
2335        let root = temp_dir("omena_bridge_recorded_shard_verdict")?;
2336        fs::write(root.join("package.json"), r#"{"name":"workspace"}"#)?;
2337        let style = root.join("tokens.scss");
2338        fs::write(style.as_path(), "$brand: #0af;\n")?;
2339        let resolved = path_to_file_uri(style.as_path());
2340        let storage = fixture_cache_storage(style.as_path());
2341        let cache_context = OmenaBridgeExternalSifCacheContextV0::default();
2342
2343        clear_external_sif_memory_cache_for_storage_for_test(&storage);
2344        let unsigned =
2345            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
2346                resolved.as_str(),
2347                &cache_context,
2348                Some(&storage),
2349            )?;
2350        let cache_dir = external_sif_cache_dir_for_path(style.as_path(), Some(&storage))
2351            .ok_or_else(|| std::io::Error::other("cache dir"))?;
2352        write_fixture_recorded_shard_verdict(&storage, &unsigned, OmenaSifTrustTierV1::T2)?;
2353        let verdict_dir = storage
2354            .recorded_verdict_dir()
2355            .ok_or_else(|| std::io::Error::other("verdict dir"))?;
2356
2357        let memory_hit =
2358            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_storage_and_trust(
2359                resolved.as_str(),
2360                &cache_context,
2361                Some(&storage),
2362            )?;
2363        assert_eq!(
2364            memory_hit.trust_envelope.trust_tier,
2365            OmenaSifTrustTierV1::T1
2366        );
2367        assert_eq!(
2368            memory_hit.trust_source,
2369            OmenaBridgeExternalSifTrustSourceV1::UnsignedLegacy
2370        );
2371        clear_external_sif_memory_cache_for_storage_for_test(&storage);
2372        let disk_hit =
2373            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_storage_and_trust(
2374                resolved.as_str(),
2375                &cache_context,
2376                Some(&storage),
2377            )?;
2378        assert_eq!(disk_hit.sif, unsigned);
2379        assert_eq!(disk_hit.trust_envelope.trust_tier, OmenaSifTrustTierV1::T1);
2380        assert_eq!(
2381            disk_hit.trust_source,
2382            OmenaBridgeExternalSifTrustSourceV1::UnsignedLegacy
2383        );
2384
2385        let shard_path = only_fixture_cache_shard_path(cache_dir.as_path())?;
2386        let mut elevated_shard = serde_json::from_slice::<Value>(&fs::read(shard_path)?)?;
2387        let key = elevated_shard
2388            .get("key")
2389            .and_then(Value::as_str)
2390            .ok_or_else(|| std::io::Error::other("shard key"))?
2391            .to_string();
2392        let source_hash = elevated_shard
2393            .get("sourceHash")
2394            .and_then(Value::as_str)
2395            .ok_or_else(|| std::io::Error::other("shard source hash"))?
2396            .to_string();
2397        let resolved_base_dir = elevated_shard
2398            .get("resolvedBaseDir")
2399            .and_then(Value::as_str)
2400            .ok_or_else(|| std::io::Error::other("shard resolved base"))?
2401            .to_string();
2402        let sif_hash = compute_omena_sif_artifact_hash_v1(&unsigned)?;
2403        elevated_shard["trustEnvelope"]["trustTier"] = Value::String("t2".to_string());
2404        elevated_shard["trustEnvelope"]["signature"] =
2405            serde_json::to_value(omena_sif::OmenaSifShardSignatureV1 {
2406                algorithm_version: omena_sif::OMENA_SIF_SHARD_SIGNATURE_ALGORITHM_VERSION_V1
2407                    .to_string(),
2408                reference: "fixture:keyless-attestation".to_string(),
2409                signed_payload_digest: sif_hash,
2410            })?;
2411        assert_eq!(
2412            validate_external_sif_cache_shard(
2413                &elevated_shard,
2414                key.as_str(),
2415                resolved.as_str(),
2416                source_hash.as_str(),
2417                resolved_base_dir.as_str(),
2418                Some(verdict_dir),
2419                &unsigned,
2420            ),
2421            Err(OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictSignatureVerificationFailed)
2422        );
2423
2424        let mut over_tier = elevated_shard;
2425        over_tier["trustEnvelope"]["trustTier"] = Value::String("t3".to_string());
2426        assert_eq!(
2427            validate_external_sif_cache_shard(
2428                &over_tier,
2429                key.as_str(),
2430                resolved.as_str(),
2431                source_hash.as_str(),
2432                resolved_base_dir.as_str(),
2433                Some(verdict_dir),
2434                &unsigned,
2435            ),
2436            Err(OmenaBridgeExternalSifShardRefusalV1::TierAboveRecordedVerdict)
2437        );
2438
2439        fs::remove_dir_all(root)?;
2440        Ok(())
2441    }
2442
2443    #[test]
2444    fn forged_recorded_verdict_cannot_mint_elevated_trust_for_poisoned_payload()
2445    -> Result<(), Box<dyn std::error::Error>> {
2446        let fixture = recorded_verdict_attack_fixture("forged-sidecar")?;
2447        write_fixture_recorded_shard_verdict(
2448            &fixture.storage,
2449            &fixture.poisoned_sif,
2450            OmenaSifTrustTierV1::T3,
2451        )?;
2452        let mut forged = fixture.original_shard.clone();
2453        forged["sifJson"] = Value::String(fixture.poisoned_sif_json.clone());
2454        forged["payloadDigest"] =
2455            Value::String(fixture.poisoned_payload_digest.as_str().to_string());
2456        forged["trustEnvelope"] = serde_json::to_value(OmenaSifShardTrustEnvelopeV1 {
2457            schema_version: OMENA_SIF_SHARD_TRUST_ENVELOPE_SCHEMA_VERSION_V1.to_string(),
2458            product: OMENA_SIF_SHARD_TRUST_ENVELOPE_PRODUCT_V1.to_string(),
2459            trust_tier: OmenaSifTrustTierV1::T3,
2460            payload_digest: fixture.poisoned_payload_digest.clone(),
2461            signature: Some(omena_sif::OmenaSifShardSignatureV1 {
2462                algorithm_version: omena_sif::OMENA_SIF_SHARD_SIGNATURE_ALGORITHM_VERSION_V1
2463                    .to_string(),
2464                reference: "fixture:keyless-attestation".to_string(),
2465                signed_payload_digest: fixture.poisoned_sif_hash.clone(),
2466            }),
2467            lock_binding: OmenaSifShardLockBindingV1 {
2468                canonical_url: fixture.resolved.clone(),
2469                sif_hash: fixture.poisoned_sif_hash.clone(),
2470            },
2471        })?;
2472
2473        let attack = validate_external_sif_cache_shard(
2474            &forged,
2475            fixture.key.as_str(),
2476            fixture.resolved.as_str(),
2477            fixture.source_hash.as_str(),
2478            fixture.resolved_base_dir.as_str(),
2479            Some(fixture.verdict_dir.as_path()),
2480            &fixture.poisoned_sif,
2481        );
2482        assert_eq!(
2483            attack,
2484            Err(OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictSignatureVerificationFailed),
2485            "forged local sidecar did not reach the typed signature refusal"
2486        );
2487        fs::remove_dir_all(fixture.root)?;
2488        Ok(())
2489    }
2490
2491    #[test]
2492    fn recorded_verdict_blocks_legacy_schema_downgrade_for_canonical_url()
2493    -> Result<(), Box<dyn std::error::Error>> {
2494        let fixture = recorded_verdict_attack_fixture("schema-downgrade")?;
2495        write_fixture_recorded_shard_verdict(
2496            &fixture.storage,
2497            &fixture.original_sif,
2498            OmenaSifTrustTierV1::T2,
2499        )?;
2500        let mut downgraded = fixture.original_shard.clone();
2501        downgraded["schemaVersion"] =
2502            Value::String(EXTERNAL_SIF_CACHE_LEGACY_SCHEMA_VERSION.to_string());
2503        downgraded["sifJson"] = Value::String(fixture.poisoned_sif_json.clone());
2504        downgraded["payloadDigest"] =
2505            Value::String(fixture.poisoned_payload_digest.as_str().to_string());
2506        downgraded
2507            .as_object_mut()
2508            .ok_or_else(|| std::io::Error::other("fixture shard object"))?
2509            .remove("trustEnvelope");
2510
2511        let attack = validate_external_sif_cache_shard(
2512            &downgraded,
2513            fixture.key.as_str(),
2514            fixture.resolved.as_str(),
2515            fixture.source_hash.as_str(),
2516            fixture.resolved_base_dir.as_str(),
2517            Some(fixture.verdict_dir.as_path()),
2518            &fixture.poisoned_sif,
2519        );
2520        assert_eq!(
2521            attack,
2522            Err(OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictDowngrade),
2523            "schema downgrade did not reach the typed downgrade refusal"
2524        );
2525        fs::remove_dir_all(fixture.root)?;
2526        Ok(())
2527    }
2528
2529    #[test]
2530    fn recorded_verdict_blocks_tier_downgrade_for_canonical_url()
2531    -> Result<(), Box<dyn std::error::Error>> {
2532        let fixture = recorded_verdict_attack_fixture("tier-downgrade")?;
2533        write_fixture_recorded_shard_verdict(
2534            &fixture.storage,
2535            &fixture.original_sif,
2536            OmenaSifTrustTierV1::T2,
2537        )?;
2538        let mut downgraded = fixture.original_shard.clone();
2539        downgraded["sifJson"] = Value::String(fixture.poisoned_sif_json.clone());
2540        downgraded["payloadDigest"] =
2541            Value::String(fixture.poisoned_payload_digest.as_str().to_string());
2542        downgraded["trustEnvelope"] = serde_json::to_value(OmenaSifShardTrustEnvelopeV1 {
2543            schema_version: OMENA_SIF_SHARD_TRUST_ENVELOPE_SCHEMA_VERSION_V1.to_string(),
2544            product: OMENA_SIF_SHARD_TRUST_ENVELOPE_PRODUCT_V1.to_string(),
2545            trust_tier: OmenaSifTrustTierV1::T1,
2546            payload_digest: fixture.poisoned_payload_digest.clone(),
2547            signature: None,
2548            lock_binding: OmenaSifShardLockBindingV1 {
2549                canonical_url: fixture.resolved.clone(),
2550                sif_hash: fixture.poisoned_sif_hash.clone(),
2551            },
2552        })?;
2553
2554        let attack = validate_external_sif_cache_shard(
2555            &downgraded,
2556            fixture.key.as_str(),
2557            fixture.resolved.as_str(),
2558            fixture.source_hash.as_str(),
2559            fixture.resolved_base_dir.as_str(),
2560            Some(fixture.verdict_dir.as_path()),
2561            &fixture.poisoned_sif,
2562        );
2563        assert_eq!(
2564            attack,
2565            Err(OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictDowngrade),
2566            "tier downgrade did not reach the typed downgrade refusal"
2567        );
2568        fs::remove_dir_all(fixture.root)?;
2569        Ok(())
2570    }
2571
2572    #[test]
2573    fn omena_published_bundle_is_the_only_fixture_path_to_elevated_advisory_tier()
2574    -> Result<(), Box<dyn std::error::Error>> {
2575        const BUNDLE_SHA256: &str =
2576            "0c99e37ac1b1d3cbfd677416a74218c9a1ca8e28c3aac95c7614549f3b3b0ce1";
2577        let root = temp_dir("verified-keyless-shard")?;
2578        let storage = OmenaBridgeExternalSifStorageV0::from_workspace_cache_root(root.clone());
2579        let verdict_dir = storage
2580            .recorded_verdict_dir()
2581            .ok_or_else(|| std::io::Error::other("verdict dir"))?;
2582        let bundle_dir = verdict_dir.join(EXTERNAL_SIF_RECORDED_BUNDLE_DIR_V1);
2583        fs::create_dir_all(bundle_dir.as_path())?;
2584        let bundle_reference = format!(
2585            "{EXTERNAL_SIF_RECORDED_BUNDLE_DIR_V1}/{BUNDLE_SHA256}{EXTERNAL_SIF_RECORDED_BUNDLE_SUFFIX_V1}"
2586        );
2587        let bundle_path = verdict_dir.join(bundle_reference.as_str());
2588        let bundle_bytes =
2589            include_bytes!("../tests/fixtures/published-sif-attestation.sigstore.json");
2590        fs::write(bundle_path.as_path(), bundle_bytes)?;
2591        let sif_source =
2592            include_str!("../tests/fixtures/published-sif-attestation.sif.json").trim_end();
2593        let sif = read_omena_sif_json_v1(sif_source)?;
2594        write_fixture_recorded_shard_verdict_with_reference(
2595            &storage,
2596            &sif,
2597            OmenaSifTrustTierV1::T3,
2598            bundle_reference.as_str(),
2599        )?;
2600
2601        let elevated = external_sif_result_with_recorded_verdict(sif.clone(), Some(verdict_dir))?;
2602        assert_eq!(elevated.trust_envelope.trust_tier, OmenaSifTrustTierV1::T3);
2603        assert_eq!(
2604            elevated.trust_source,
2605            OmenaBridgeExternalSifTrustSourceV1::RecordedVerdict
2606        );
2607        let payload_digest = compute_omena_sif_leaf_hash_v1(sif_source.as_bytes());
2608        let shard = json!({
2609            "schemaVersion": EXTERNAL_SIF_CACHE_SCHEMA_VERSION,
2610            "product": EXTERNAL_SIF_CACHE_PRODUCT,
2611            "key": "fixture-key",
2612            "canonicalUrl": sif.canonical_url,
2613            "sourceHash": "fixture-source-hash",
2614            "resolvedBaseDir": "fixture-base",
2615            "payloadDigest": payload_digest,
2616            "trustEnvelope": elevated.trust_envelope,
2617            "sifJson": sif_source,
2618        });
2619        let validated = validate_external_sif_cache_shard(
2620            &shard,
2621            "fixture-key",
2622            sif.canonical_url.as_str(),
2623            "fixture-source-hash",
2624            "fixture-base",
2625            Some(verdict_dir),
2626            &sif,
2627        )
2628        .map_err(|refusal| {
2629            std::io::Error::other(format!("verified fixture shard was refused: {refusal:?}"))
2630        })?;
2631        assert_eq!(validated.trust_envelope.trust_tier, OmenaSifTrustTierV1::T3);
2632
2633        fs::write(bundle_path.as_path(), b"{}")?;
2634        assert_eq!(
2635            validate_external_sif_cache_shard(
2636                &shard,
2637                "fixture-key",
2638                sif.canonical_url.as_str(),
2639                "fixture-source-hash",
2640                "fixture-base",
2641                Some(verdict_dir),
2642                &sif,
2643            ),
2644            Err(OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictSignatureVerificationFailed)
2645        );
2646        let downgraded = external_sif_result_with_recorded_verdict(sif, Some(verdict_dir))?;
2647        assert_eq!(
2648            downgraded.trust_envelope.trust_tier,
2649            OmenaSifTrustTierV1::T1
2650        );
2651        assert_eq!(
2652            downgraded.trust_source,
2653            OmenaBridgeExternalSifTrustSourceV1::UnsignedLegacy
2654        );
2655
2656        fs::remove_dir_all(root)?;
2657        Ok(())
2658    }
2659
2660    #[test]
2661    fn published_bundle_cannot_authorize_a_different_recorded_tier()
2662    -> Result<(), Box<dyn std::error::Error>> {
2663        const BUNDLE_SHA256: &str =
2664            "0c99e37ac1b1d3cbfd677416a74218c9a1ca8e28c3aac95c7614549f3b3b0ce1";
2665        let root = temp_dir("published-bundle-tier-binding")?;
2666        let storage = OmenaBridgeExternalSifStorageV0::from_workspace_cache_root(root.clone());
2667        let verdict_dir = storage
2668            .recorded_verdict_dir()
2669            .ok_or_else(|| std::io::Error::other("verdict dir"))?;
2670        let bundle_reference = format!(
2671            "{EXTERNAL_SIF_RECORDED_BUNDLE_DIR_V1}/{BUNDLE_SHA256}{EXTERNAL_SIF_RECORDED_BUNDLE_SUFFIX_V1}"
2672        );
2673        let bundle_path = verdict_dir.join(bundle_reference.as_str());
2674        fs::create_dir_all(
2675            bundle_path
2676                .parent()
2677                .ok_or_else(|| std::io::Error::other("bundle parent"))?,
2678        )?;
2679        fs::write(
2680            bundle_path,
2681            include_bytes!("../tests/fixtures/published-sif-attestation.sigstore.json"),
2682        )?;
2683        let sif = read_omena_sif_json_v1(
2684            include_str!("../tests/fixtures/published-sif-attestation.sif.json").trim_end(),
2685        )?;
2686        write_fixture_recorded_shard_verdict_with_reference(
2687            &storage,
2688            &sif,
2689            OmenaSifTrustTierV1::T2,
2690            bundle_reference.as_str(),
2691        )?;
2692
2693        let result = external_sif_result_with_recorded_verdict(sif, Some(verdict_dir))?;
2694        assert_eq!(result.trust_envelope.trust_tier, OmenaSifTrustTierV1::T1);
2695        assert_eq!(
2696            result.trust_source,
2697            OmenaBridgeExternalSifTrustSourceV1::UnsignedLegacy
2698        );
2699
2700        fs::remove_dir_all(root)?;
2701        Ok(())
2702    }
2703
2704    #[test]
2705    fn published_bundle_cannot_elevate_a_different_sif_artifact()
2706    -> Result<(), Box<dyn std::error::Error>> {
2707        const BUNDLE_SHA256: &str =
2708            "0c99e37ac1b1d3cbfd677416a74218c9a1ca8e28c3aac95c7614549f3b3b0ce1";
2709        let root = temp_dir("published-bundle-artifact-substitution")?;
2710        let storage = OmenaBridgeExternalSifStorageV0::from_workspace_cache_root(root.clone());
2711        let verdict_dir = storage
2712            .recorded_verdict_dir()
2713            .ok_or_else(|| std::io::Error::other("verdict dir"))?;
2714        let bundle_dir = verdict_dir.join(EXTERNAL_SIF_RECORDED_BUNDLE_DIR_V1);
2715        fs::create_dir_all(bundle_dir.as_path())?;
2716        let bundle_reference = format!(
2717            "{EXTERNAL_SIF_RECORDED_BUNDLE_DIR_V1}/{BUNDLE_SHA256}{EXTERNAL_SIF_RECORDED_BUNDLE_SUFFIX_V1}"
2718        );
2719        fs::write(
2720            verdict_dir.join(bundle_reference.as_str()),
2721            include_bytes!("../tests/fixtures/published-sif-attestation.sigstore.json"),
2722        )?;
2723        let mut poisoned_sif = read_omena_sif_json_v1(
2724            include_str!("../tests/fixtures/published-sif-attestation.sif.json").trim_end(),
2725        )?;
2726        poisoned_sif.generator.name = "untrusted-generator".to_string();
2727        write_fixture_recorded_shard_verdict_with_reference(
2728            &storage,
2729            &poisoned_sif,
2730            OmenaSifTrustTierV1::T3,
2731            bundle_reference.as_str(),
2732        )?;
2733        let poisoned_hash = compute_omena_sif_artifact_hash_v1(&poisoned_sif)?;
2734        let verdict = load_recorded_shard_verdict(
2735            Some(verdict_dir),
2736            poisoned_sif.canonical_url.as_str(),
2737            &poisoned_hash,
2738        )
2739        .ok_or_else(|| std::io::Error::other("poisoned verdict"))?;
2740
2741        assert_eq!(
2742            verify_recorded_shard_verdict(Some(verdict_dir), &verdict),
2743            Err(OmenaBridgeExternalSifShardRefusalV1::RecordedVerdictSignatureVerificationFailed)
2744        );
2745        let result = external_sif_result_with_recorded_verdict(poisoned_sif, Some(verdict_dir))?;
2746        assert_eq!(result.trust_envelope.trust_tier, OmenaSifTrustTierV1::T1);
2747        assert_eq!(
2748            result.trust_source,
2749            OmenaBridgeExternalSifTrustSourceV1::UnsignedLegacy
2750        );
2751
2752        fs::remove_dir_all(root)?;
2753        Ok(())
2754    }
2755
2756    #[test]
2757    fn same_version_legacy_shard_without_trust_fields_remains_workspace_local()
2758    -> Result<(), Box<dyn std::error::Error>> {
2759        let root = temp_dir("omena_bridge_legacy_shard")?;
2760        fs::write(root.join("package.json"), r#"{"name":"workspace"}"#)?;
2761        let style = root.join("tokens.scss");
2762        fs::write(style.as_path(), "$brand: #0af;\n")?;
2763        let resolved = path_to_file_uri(style.as_path());
2764        let storage = fixture_cache_storage(style.as_path());
2765        let cache_context = OmenaBridgeExternalSifCacheContextV0::default();
2766        clear_external_sif_memory_cache_for_storage_for_test(&storage);
2767        let fresh =
2768            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_storage_and_trust(
2769                resolved.as_str(),
2770                &cache_context,
2771                Some(&storage),
2772            )?;
2773        let cache_dir = external_sif_cache_dir_for_path(style.as_path(), Some(&storage))
2774            .ok_or_else(|| std::io::Error::other("cache dir"))?;
2775        let shard_path = only_fixture_cache_shard_path(cache_dir.as_path())?;
2776        let mut legacy = serde_json::from_slice::<Value>(&fs::read(shard_path.as_path())?)?;
2777        legacy["schemaVersion"] =
2778            Value::String(EXTERNAL_SIF_CACHE_LEGACY_SCHEMA_VERSION.to_string());
2779        legacy
2780            .as_object_mut()
2781            .ok_or_else(|| std::io::Error::other("legacy shard object"))?
2782            .remove("trustEnvelope");
2783        fs::write(
2784            shard_path.as_path(),
2785            write_omena_canonical_json_bytes_v1(&legacy)?,
2786        )?;
2787        clear_external_sif_memory_cache_for_storage_for_test(&storage);
2788        let loaded =
2789            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_storage_and_trust(
2790                resolved.as_str(),
2791                &cache_context,
2792                Some(&storage),
2793            )?;
2794        assert_eq!(loaded.sif, fresh.sif);
2795        assert_eq!(loaded.trust_envelope.trust_tier, OmenaSifTrustTierV1::T1);
2796        assert_eq!(
2797            loaded.trust_source,
2798            OmenaBridgeExternalSifTrustSourceV1::UnsignedLegacy
2799        );
2800        let _ = fs::remove_dir_all(root);
2801        Ok(())
2802    }
2803
2804    #[test]
2805    fn external_sif_disk_cache_root_carries_self_ignore_markers()
2806    -> Result<(), Box<dyn std::error::Error>> {
2807        let root = temp_dir("omena_bridge_external_sif_cache_markers")?;
2808        fs::write(root.join("package.json"), r#"{"name":"workspace"}"#)?;
2809        let style = root.join("tokens.scss");
2810        fs::write(style.as_path(), "$brand: #0af;\n")?;
2811
2812        let cache_storage = fixture_cache_storage(style.as_path());
2813        generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
2814            style.to_string_lossy().as_ref(),
2815            &OmenaBridgeExternalSifCacheContextV0::default(),
2816            Some(&cache_storage),
2817        )?;
2818
2819        let cache_dir = external_sif_cache_dir_for_path(style.as_path(), Some(&cache_storage))
2820            .ok_or_else(|| std::io::Error::other("cache dir"))?;
2821        let cache_root = cache_dir
2822            .parent()
2823            .ok_or_else(|| std::io::Error::other("cache root"))?;
2824        assert_eq!(
2825            fs::read(cache_root.join(".gitignore"))?,
2826            b"# machine-generated omena cache - safe to delete\n*\n",
2827            "external SIF cache root {} must self-ignore generated files",
2828            cache_root.display()
2829        );
2830        assert_eq!(
2831            fs::read(cache_root.join("CACHEDIR.TAG"))?,
2832            b"Signature: 8a477f597d28d172789f06886806bc55\n# This directory is an omena cache; contents are regenerable.\n",
2833            "external SIF cache root {} must carry the standard cache tag",
2834            cache_root.display()
2835        );
2836        let attribution = serde_json::from_slice::<Value>(&fs::read(
2837            cache_root.join(".omena-cache-owner.json"),
2838        )?)?;
2839        assert_eq!(
2840            attribution.get("product").and_then(Value::as_str),
2841            Some("omena.cache-root-attribution")
2842        );
2843        assert!(
2844            attribution
2845                .get("workspaceIdentity")
2846                .and_then(Value::as_str)
2847                .is_some_and(|identity| !identity.is_empty())
2848        );
2849        let _ = fs::remove_dir_all(root);
2850        Ok(())
2851    }
2852
2853    #[test]
2854    fn errors_gracefully_for_missing_resolved_style_module() {
2855        let missing = std::env::temp_dir().join("omena_bridge_sif_missing/does-not-exist.scss");
2856        let result =
2857            generate_omena_bridge_sif_for_resolved_style_path(missing.to_string_lossy().as_ref());
2858        assert!(result.is_err(), "expected error for missing entry");
2859    }
2860
2861    #[test]
2862    fn errors_gracefully_for_empty_resolved_path() {
2863        let result = generate_omena_bridge_sif_for_resolved_style_path("");
2864        assert!(result.is_err(), "expected error for empty path");
2865    }
2866
2867    #[test]
2868    fn resolves_tsconfig_path_alias_style_candidates() -> Result<(), Box<dyn std::error::Error>> {
2869        let root = temp_dir("omena_bridge_style_alias")?;
2870        let source = root.join("src/App.tsx");
2871        let style = root.join("src/styles/Button.module.scss");
2872        fs::create_dir_all(
2873            style
2874                .parent()
2875                .ok_or_else(|| std::io::Error::other("parent"))?,
2876        )?;
2877        fs::write(&source, "")?;
2878        fs::write(&style, ".root {}")?;
2879        fs::write(
2880            root.join("tsconfig.json"),
2881            r#"{"compilerOptions":{"baseUrl":".","paths":{"@styles/*":["src/styles/*"]}}}"#,
2882        )?;
2883
2884        let uri = resolve_omena_bridge_style_uri_for_specifier(
2885            path_to_file_uri(source.as_path()).as_str(),
2886            Some(path_to_file_uri(root.as_path()).as_str()),
2887            "@styles/Button.module.scss",
2888        );
2889
2890        assert_eq!(
2891            uri.as_deref(),
2892            Some(path_to_file_uri(style.as_path()).as_str())
2893        );
2894        let _ = fs::remove_dir_all(root);
2895        Ok(())
2896    }
2897
2898    #[test]
2899    fn resolves_tsconfig_extends_path_alias_style_candidates()
2900    -> Result<(), Box<dyn std::error::Error>> {
2901        let root = temp_dir("omena_bridge_style_alias_extends")?;
2902        let source = root.join("src/App.tsx");
2903        let style = root.join("src/shared/Button.module.scss");
2904        let config_dir = root.join("config");
2905        fs::create_dir_all(
2906            style
2907                .parent()
2908                .ok_or_else(|| std::io::Error::other("parent"))?,
2909        )?;
2910        fs::create_dir_all(config_dir.as_path())?;
2911        fs::write(&source, "")?;
2912        fs::write(&style, ".root {}")?;
2913        fs::write(
2914            config_dir.join("base.json"),
2915            r#"{"compilerOptions":{"baseUrl":"..","paths":{"$shared/*":["src/shared/*"]}}}"#,
2916        )?;
2917        fs::write(root.join("tsconfig.json"), r#"{"extends":"./config/base"}"#)?;
2918
2919        let uri = resolve_omena_bridge_style_uri_for_specifier(
2920            path_to_file_uri(source.as_path()).as_str(),
2921            Some(path_to_file_uri(root.as_path()).as_str()),
2922            "$shared/Button.module.scss",
2923        );
2924
2925        assert_eq!(
2926            uri.as_deref(),
2927            Some(path_to_file_uri(style.as_path()).as_str())
2928        );
2929        let _ = fs::remove_dir_all(root);
2930        Ok(())
2931    }
2932
2933    #[test]
2934    fn tsconfig_extends_child_paths_override_parent_paths() -> Result<(), Box<dyn std::error::Error>>
2935    {
2936        let root = temp_dir("omena_bridge_style_alias_extends_override")?;
2937        let source = root.join("src/App.tsx");
2938        let parent_style = root.join("src/parent/Button.module.scss");
2939        let child_style = root.join("src/child/Button.module.scss");
2940        fs::create_dir_all(
2941            parent_style
2942                .parent()
2943                .ok_or_else(|| std::io::Error::other("parent"))?,
2944        )?;
2945        fs::create_dir_all(
2946            child_style
2947                .parent()
2948                .ok_or_else(|| std::io::Error::other("child"))?,
2949        )?;
2950        fs::write(&source, "")?;
2951        fs::write(&parent_style, ".root { color: red; }")?;
2952        fs::write(&child_style, ".root { color: green; }")?;
2953        fs::write(
2954            root.join("base.json"),
2955            r#"{"compilerOptions":{"baseUrl":".","paths":{"$shared/*":["src/parent/*"]}}}"#,
2956        )?;
2957        fs::write(
2958            root.join("tsconfig.json"),
2959            r#"{"extends":"./base.json","compilerOptions":{"baseUrl":".","paths":{"$shared/*":["src/child/*"]}}}"#,
2960        )?;
2961
2962        let uri = resolve_omena_bridge_style_uri_for_specifier(
2963            path_to_file_uri(source.as_path()).as_str(),
2964            Some(path_to_file_uri(root.as_path()).as_str()),
2965            "$shared/Button.module.scss",
2966        );
2967
2968        assert_eq!(
2969            uri.as_deref(),
2970            Some(path_to_file_uri(child_style.as_path()).as_str())
2971        );
2972        let _ = fs::remove_dir_all(root);
2973        Ok(())
2974    }
2975
2976    #[test]
2977    fn resolves_vite_bundler_alias_style_candidates() -> Result<(), Box<dyn std::error::Error>> {
2978        let root = temp_dir("omena_bridge_style_bundler_alias")?;
2979        let source = root.join("src/App.tsx");
2980        let style = root.join("src/styles/Button.module.scss");
2981        fs::create_dir_all(
2982            style
2983                .parent()
2984                .ok_or_else(|| std::io::Error::other("parent"))?,
2985        )?;
2986        fs::write(&source, "")?;
2987        fs::write(&style, ".root {}")?;
2988        fs::write(
2989            root.join("vite.config.ts"),
2990            r#"export default { resolve: { alias: { "@styles": "./src/styles" } } };"#,
2991        )?;
2992
2993        let uri = resolve_omena_bridge_style_uri_for_specifier(
2994            path_to_file_uri(source.as_path()).as_str(),
2995            Some(path_to_file_uri(root.as_path()).as_str()),
2996            "@styles/Button.module.scss",
2997        );
2998
2999        assert_eq!(
3000            uri.as_deref(),
3001            Some(path_to_file_uri(style.as_path()).as_str())
3002        );
3003        let _ = fs::remove_dir_all(root);
3004        Ok(())
3005    }
3006
3007    #[test]
3008    fn resolves_webpack_exact_bundler_alias_style_candidates()
3009    -> Result<(), Box<dyn std::error::Error>> {
3010        let root = temp_dir("omena_bridge_style_bundler_exact_alias")?;
3011        let source = root.join("src/App.tsx");
3012        let style = root.join("src/styles/index.module.scss");
3013        fs::create_dir_all(
3014            style
3015                .parent()
3016                .ok_or_else(|| std::io::Error::other("parent"))?,
3017        )?;
3018        fs::write(&source, "")?;
3019        fs::write(&style, ".root {}")?;
3020        fs::write(
3021            root.join("webpack.config.js"),
3022            r#"module.exports = { resolve: { alias: [{ find: "@theme$", replacement: "./src/styles/index.module.scss" }] } };"#,
3023        )?;
3024
3025        let exact_uri = resolve_omena_bridge_style_uri_for_specifier(
3026            path_to_file_uri(source.as_path()).as_str(),
3027            Some(path_to_file_uri(root.as_path()).as_str()),
3028            "@theme",
3029        );
3030        let prefix_uri = resolve_omena_bridge_style_uri_for_specifier(
3031            path_to_file_uri(source.as_path()).as_str(),
3032            Some(path_to_file_uri(root.as_path()).as_str()),
3033            "@theme/Button.module.scss",
3034        );
3035
3036        assert_eq!(
3037            exact_uri.as_deref(),
3038            Some(path_to_file_uri(style.as_path()).as_str())
3039        );
3040        assert!(prefix_uri.is_none());
3041        let _ = fs::remove_dir_all(root);
3042        Ok(())
3043    }
3044
3045    #[test]
3046    fn resolves_sass_style_candidates_without_legacy_language_filter()
3047    -> Result<(), Box<dyn std::error::Error>> {
3048        let root = temp_dir("omena_bridge_style_sass")?;
3049        let source = root.join("src/App.tsx");
3050        let style = root.join("src/Button.module.sass");
3051        fs::create_dir_all(
3052            source
3053                .parent()
3054                .ok_or_else(|| std::io::Error::other("parent"))?,
3055        )?;
3056        fs::write(&source, "")?;
3057        fs::write(&style, ".root\n  color: red\n")?;
3058
3059        let uri = resolve_omena_bridge_style_uri_for_specifier(
3060            path_to_file_uri(source.as_path()).as_str(),
3061            Some(path_to_file_uri(root.as_path()).as_str()),
3062            "./Button.module.sass",
3063        );
3064
3065        assert_eq!(
3066            uri.as_deref(),
3067            Some(path_to_file_uri(style.as_path()).as_str())
3068        );
3069        let _ = fs::remove_dir_all(root);
3070        Ok(())
3071    }
3072
3073    #[test]
3074    fn resolves_package_style_candidates_through_omena_resolver()
3075    -> Result<(), Box<dyn std::error::Error>> {
3076        let root = temp_dir("omena_bridge_style_package")?;
3077        let source = root.join("src/App.module.scss");
3078        let package_root = root.join("node_modules/@design/tokens");
3079        let style = package_root.join("src/index.scss");
3080        fs::create_dir_all(
3081            style
3082                .parent()
3083                .ok_or_else(|| std::io::Error::other("parent"))?,
3084        )?;
3085        fs::create_dir_all(
3086            source
3087                .parent()
3088                .ok_or_else(|| std::io::Error::other("source parent"))?,
3089        )?;
3090        fs::write(&source, "@use \"@design/tokens\";")?;
3091        fs::write(
3092            package_root.join("package.json"),
3093            r#"{"sass":"src/index.scss"}"#,
3094        )?;
3095        fs::write(&style, "$gap: 1rem;")?;
3096
3097        let uri = resolve_omena_bridge_style_uri_for_specifier(
3098            path_to_file_uri(source.as_path()).as_str(),
3099            Some(path_to_file_uri(root.as_path()).as_str()),
3100            "@design/tokens",
3101        );
3102
3103        assert_eq!(
3104            uri.as_deref(),
3105            Some(path_to_file_uri(style.as_path()).as_str())
3106        );
3107        let _ = fs::remove_dir_all(root);
3108        Ok(())
3109    }
3110
3111    #[test]
3112    fn resolves_sass_pkg_style_candidates_through_manifest_discovery()
3113    -> Result<(), Box<dyn std::error::Error>> {
3114        let root = temp_dir("omena_bridge_style_pkg_manifest")?;
3115        let source = root.join("src/App.module.scss");
3116        let package_root = root.join("node_modules/@design/tokens");
3117        let style = package_root.join("dist/theme.scss");
3118        fs::create_dir_all(
3119            style
3120                .parent()
3121                .ok_or_else(|| std::io::Error::other("style parent"))?,
3122        )?;
3123        fs::create_dir_all(
3124            source
3125                .parent()
3126                .ok_or_else(|| std::io::Error::other("source parent"))?,
3127        )?;
3128        fs::write(&source, "@use \"pkg:@design/tokens/theme\";")?;
3129        fs::write(
3130            package_root.join("package.json"),
3131            r#"{"exports":{"./theme":{"sass":"./dist/theme.scss"}}}"#,
3132        )?;
3133        fs::write(&style, "$gap: 1rem;")?;
3134
3135        let uri = resolve_omena_bridge_style_uri_for_specifier(
3136            path_to_file_uri(source.as_path()).as_str(),
3137            Some(path_to_file_uri(root.as_path()).as_str()),
3138            "pkg:@design/tokens/theme",
3139        );
3140
3141        assert_eq!(
3142            uri.as_deref(),
3143            Some(path_to_file_uri(style.as_path()).as_str())
3144        );
3145        let _ = fs::remove_dir_all(root);
3146        Ok(())
3147    }
3148
3149    #[test]
3150    fn resolves_package_import_style_candidates_through_workspace_manifests()
3151    -> Result<(), Box<dyn std::error::Error>> {
3152        let root = temp_dir("omena_bridge_style_package_import_manifest")?;
3153        let source = root.join("src/App.module.scss");
3154        let package_root = root.join("node_modules/@design/tokens");
3155        let style = package_root.join("dist/theme.scss");
3156        fs::create_dir_all(
3157            style
3158                .parent()
3159                .ok_or_else(|| std::io::Error::other("style parent"))?,
3160        )?;
3161        fs::create_dir_all(
3162            source
3163                .parent()
3164                .ok_or_else(|| std::io::Error::other("source parent"))?,
3165        )?;
3166        fs::write(&source, "@use \"#theme\" as tokens;")?;
3167        fs::write(
3168            root.join("package.json"),
3169            r##"{"imports":{"#theme":"@design/tokens/theme"}}"##,
3170        )?;
3171        fs::write(
3172            package_root.join("package.json"),
3173            r#"{"exports":{"./theme":{"sass":"./dist/theme.scss"}}}"#,
3174        )?;
3175        fs::write(&style, "$gap: 1rem;")?;
3176
3177        let uri = resolve_omena_bridge_style_uri_for_specifier(
3178            path_to_file_uri(source.as_path()).as_str(),
3179            Some(path_to_file_uri(root.as_path()).as_str()),
3180            "#theme",
3181        );
3182
3183        assert_eq!(
3184            uri.as_deref(),
3185            Some(path_to_file_uri(style.as_path()).as_str())
3186        );
3187        let _ = fs::remove_dir_all(root);
3188        Ok(())
3189    }
3190
3191    #[cfg(unix)]
3192    #[test]
3193    fn resolves_symlinked_package_style_candidates_to_canonical_uri()
3194    -> Result<(), Box<dyn std::error::Error>> {
3195        let root = temp_dir("omena_bridge_style_symlinked_package")?;
3196        let source = root.join("src/App.module.scss");
3197        let real_package = root.join(".pnpm/@design+tokens@1.0.0/node_modules/@design/tokens");
3198        let linked_scope = root.join("node_modules/@design");
3199        let linked_package = linked_scope.join("tokens");
3200        let style = real_package.join("src/index.scss");
3201        fs::create_dir_all(
3202            style
3203                .parent()
3204                .ok_or_else(|| std::io::Error::other("style parent"))?,
3205        )?;
3206        fs::create_dir_all(
3207            source
3208                .parent()
3209                .ok_or_else(|| std::io::Error::other("source parent"))?,
3210        )?;
3211        fs::create_dir_all(linked_scope.as_path())?;
3212        fs::write(&source, "@use \"@design/tokens\";")?;
3213        fs::write(
3214            real_package.join("package.json"),
3215            r#"{"sass":"src/index.scss"}"#,
3216        )?;
3217        fs::write(&style, "$gap: 1rem;")?;
3218        std::os::unix::fs::symlink(real_package.as_path(), linked_package.as_path())?;
3219
3220        let uri = resolve_omena_bridge_style_uri_for_specifier(
3221            path_to_file_uri(source.as_path()).as_str(),
3222            Some(path_to_file_uri(root.as_path()).as_str()),
3223            "@design/tokens",
3224        );
3225        let expected_uri = path_to_file_uri(fs::canonicalize(style)?.as_path());
3226
3227        assert_eq!(uri.as_deref(), Some(expected_uri.as_str()));
3228        let _ = fs::remove_dir_all(root);
3229        Ok(())
3230    }
3231
3232    #[test]
3233    fn does_not_fabricate_missing_package_style_candidates()
3234    -> Result<(), Box<dyn std::error::Error>> {
3235        let root = temp_dir("omena_bridge_style_missing_package")?;
3236        let source = root.join("src/App.tsx");
3237        fs::create_dir_all(
3238            source
3239                .parent()
3240                .ok_or_else(|| std::io::Error::other("parent"))?,
3241        )?;
3242        fs::write(&source, "")?;
3243
3244        let uri = resolve_omena_bridge_style_uri_for_specifier(
3245            path_to_file_uri(source.as_path()).as_str(),
3246            Some(path_to_file_uri(root.as_path()).as_str()),
3247            "@design/tokens",
3248        );
3249
3250        assert!(uri.is_none(), "{uri:?}");
3251        let _ = fs::remove_dir_all(root);
3252        Ok(())
3253    }
3254
3255    #[test]
3256    fn emits_percent_encoded_file_uris_for_route_group_paths()
3257    -> Result<(), Box<dyn std::error::Error>> {
3258        let root = temp_dir("omena_bridge_style_route_group")?;
3259        let source = root.join("app/(marketing)/page.tsx");
3260        let style = root.join("app/(marketing)/Card.module.scss");
3261        fs::create_dir_all(
3262            source
3263                .parent()
3264                .ok_or_else(|| std::io::Error::other("parent"))?,
3265        )?;
3266        fs::write(&source, "")?;
3267        fs::write(&style, ".card {}")?;
3268
3269        let uri = resolve_omena_bridge_style_uri_for_specifier(
3270            path_to_file_uri(source.as_path()).as_str(),
3271            Some(path_to_file_uri(root.as_path()).as_str()),
3272            "./Card.module.scss",
3273        )
3274        .ok_or_else(|| std::io::Error::other("route group style should resolve"))?;
3275
3276        assert!(uri.contains("%28marketing%29"), "{uri}");
3277        assert_eq!(uri, path_to_file_uri(style.as_path()));
3278        let _ = fs::remove_dir_all(root);
3279        Ok(())
3280    }
3281
3282    #[test]
3283    fn declares_bridge_owned_style_resolution_boundary() {
3284        let summary = summarize_omena_bridge_style_resolution_boundary();
3285
3286        assert_eq!(summary.product, "omena-bridge.style-resolution");
3287        assert_eq!(summary.owner_crate, "omena-bridge");
3288        assert!(summary.supported_specifier_kinds.contains(&"tsconfigPaths"));
3289        assert!(
3290            summary
3291                .supported_specifier_kinds
3292                .contains(&"bundlerAliases")
3293        );
3294        assert!(summary.supported_specifier_kinds.contains(&"npmPackages"));
3295        assert!(
3296            summary
3297                .request_path_policy
3298                .contains(&"pathAliasResolutionFollowsRelativeTsconfigExtends")
3299        );
3300        assert!(
3301            summary
3302                .request_path_policy
3303                .contains(&"bundlerAliasResolutionUsesLiteralViteWebpackConfig")
3304        );
3305        assert!(
3306            summary
3307                .request_path_policy
3308                .contains(&"lspServerOwnsOnlyDocumentRoutingAndUriRangeMapping")
3309        );
3310    }
3311
3312    #[test]
3313    fn resolves_nested_next_config_alias_style_candidates() -> Result<(), Box<dyn std::error::Error>>
3314    {
3315        let root = temp_dir("omena_bridge_style_next_nested")?;
3316        let app_dir = root.join("apps/web");
3317        let source = app_dir.join("src/App.tsx");
3318        let style = app_dir.join("src/styles/Button.module.scss");
3319        fs::create_dir_all(
3320            style
3321                .parent()
3322                .ok_or_else(|| std::io::Error::other("style parent"))?,
3323        )?;
3324        fs::write(&source, "")?;
3325        fs::write(&style, ".root {}")?;
3326        fs::write(
3327            app_dir.join("next.config.mjs"),
3328            r#"export default { resolve: { alias: { "@styles": "./src/styles" } } };"#,
3329        )?;
3330
3331        let uri = resolve_omena_bridge_style_uri_for_specifier(
3332            path_to_file_uri(source.as_path()).as_str(),
3333            Some(path_to_file_uri(root.as_path()).as_str()),
3334            "@styles/Button.module.scss",
3335        );
3336
3337        assert_eq!(
3338            uri.as_deref(),
3339            Some(path_to_file_uri(style.as_path()).as_str())
3340        );
3341        let _ = fs::remove_dir_all(root);
3342        Ok(())
3343    }
3344
3345    #[test]
3346    fn resolves_tilde_package_style_candidates() -> Result<(), Box<dyn std::error::Error>> {
3347        let root = temp_dir("omena_bridge_style_tilde_package")?;
3348        let source = root.join("src/App.module.scss");
3349        let package_root = root.join("node_modules/@scope/theme");
3350        let style = package_root.join("index.scss");
3351        fs::create_dir_all(
3352            source
3353                .parent()
3354                .ok_or_else(|| std::io::Error::other("source parent"))?,
3355        )?;
3356        fs::create_dir_all(package_root.as_path())?;
3357        fs::write(&source, "@use \"~@scope/theme\";")?;
3358        fs::write(
3359            package_root.join("package.json"),
3360            r#"{"sass":"./index.scss"}"#,
3361        )?;
3362        fs::write(&style, "$brand: red;")?;
3363
3364        let uri = resolve_omena_bridge_style_uri_for_specifier(
3365            path_to_file_uri(source.as_path()).as_str(),
3366            Some(path_to_file_uri(root.as_path()).as_str()),
3367            "~@scope/theme",
3368        );
3369
3370        assert_eq!(
3371            uri.as_deref(),
3372            Some(path_to_file_uri(style.as_path()).as_str())
3373        );
3374        let _ = fs::remove_dir_all(root);
3375        Ok(())
3376    }
3377
3378    fn temp_dir(prefix: &str) -> Result<PathBuf, Box<dyn std::error::Error>> {
3379        let suffix = SystemTime::now()
3380            .duration_since(SystemTime::UNIX_EPOCH)?
3381            .as_nanos();
3382        let path = std::env::temp_dir().join(format!("{prefix}_{suffix}"));
3383        fs::create_dir_all(path.as_path())?;
3384        Ok(path)
3385    }
3386
3387    fn fixture_cache_storage(path: &Path) -> OmenaBridgeExternalSifStorageV0 {
3388        OmenaBridgeExternalSifStorageV0::from_workspace_cache_root(
3389            path.parent()
3390                .unwrap_or_else(|| Path::new("."))
3391                .join(".cache")
3392                .join("omena"),
3393        )
3394    }
3395
3396    fn only_fixture_cache_shard_path(
3397        cache_dir: &Path,
3398    ) -> Result<PathBuf, Box<dyn std::error::Error>> {
3399        let mut shards = fs::read_dir(cache_dir)?
3400            .flatten()
3401            .map(|entry| entry.path())
3402            .filter(|path| {
3403                path.extension()
3404                    .is_some_and(|extension| extension == "json")
3405            })
3406            .collect::<Vec<_>>();
3407        shards.sort();
3408        if shards.len() != 1 {
3409            return Err(std::io::Error::other(format!(
3410                "expected one fixture cache shard, found {}",
3411                shards.len()
3412            ))
3413            .into());
3414        }
3415        Ok(shards.remove(0))
3416    }
3417
3418    fn write_fixture_recorded_shard_verdict(
3419        storage: &OmenaBridgeExternalSifStorageV0,
3420        sif: &OmenaSifV1,
3421        trust_tier: OmenaSifTrustTierV1,
3422    ) -> Result<(), Box<dyn std::error::Error>> {
3423        write_fixture_recorded_shard_verdict_with_reference(
3424            storage,
3425            sif,
3426            trust_tier,
3427            "fixture:keyless-attestation",
3428        )
3429    }
3430
3431    fn write_fixture_recorded_shard_verdict_with_reference(
3432        storage: &OmenaBridgeExternalSifStorageV0,
3433        sif: &OmenaSifV1,
3434        trust_tier: OmenaSifTrustTierV1,
3435        signature_reference: &str,
3436    ) -> Result<(), Box<dyn std::error::Error>> {
3437        let sif_hash = compute_omena_sif_artifact_hash_v1(sif)?;
3438        let verdict = OmenaSifShardRecordedVerdictV1 {
3439            schema_version: omena_sif::OMENA_SIF_SHARD_RECORDED_VERDICT_SCHEMA_VERSION_V1
3440                .to_string(),
3441            product: omena_sif::OMENA_SIF_SHARD_RECORDED_VERDICT_PRODUCT_V1.to_string(),
3442            verification_owner: omena_sif::OMENA_SIF_SHARD_VERIFICATION_OWNER_V1.to_string(),
3443            canonical_url: sif.canonical_url.clone(),
3444            sif_hash: sif_hash.clone(),
3445            trust_tier,
3446            signature: omena_sif::OmenaSifShardSignatureV1 {
3447                algorithm_version: omena_sif::OMENA_SIF_SHARD_SIGNATURE_ALGORITHM_VERSION_V1
3448                    .to_string(),
3449                reference: signature_reference.to_string(),
3450                signed_payload_digest: sif_hash.clone(),
3451            },
3452        };
3453        omena_sif::validate_omena_sif_shard_recorded_verdict_v1(&verdict)?;
3454        let verdict_dir = storage
3455            .recorded_verdict_dir()
3456            .ok_or_else(|| std::io::Error::other("verdict dir"))?;
3457        fs::create_dir_all(verdict_dir)?;
3458        let address = compute_omena_sif_shard_recorded_verdict_address_v1(
3459            sif.canonical_url.as_str(),
3460            &sif_hash,
3461        )?;
3462        let hex = address
3463            .as_str()
3464            .strip_prefix("blake3:")
3465            .ok_or_else(|| std::io::Error::other("verdict address"))?;
3466        let source = omena_sif::write_omena_sif_shard_recorded_verdict_json_v1(&verdict)?;
3467        fs::write(verdict_dir.join(format!("{hex}.json")), source)?;
3468        Ok(())
3469    }
3470
3471    struct RecordedVerdictAttackFixture {
3472        root: PathBuf,
3473        resolved: String,
3474        storage: OmenaBridgeExternalSifStorageV0,
3475        verdict_dir: PathBuf,
3476        key: String,
3477        source_hash: String,
3478        resolved_base_dir: String,
3479        original_shard: Value,
3480        original_sif: OmenaSifV1,
3481        poisoned_sif: OmenaSifV1,
3482        poisoned_sif_json: String,
3483        poisoned_payload_digest: omena_sif::OmenaSifDigestV1,
3484        poisoned_sif_hash: omena_sif::OmenaSifDigestV1,
3485    }
3486
3487    fn recorded_verdict_attack_fixture(
3488        label: &str,
3489    ) -> Result<RecordedVerdictAttackFixture, Box<dyn std::error::Error>> {
3490        let root = temp_dir(label)?;
3491        fs::write(root.join("package.json"), r#"{"name":"workspace"}"#)?;
3492        let style = root.join("tokens.scss");
3493        fs::write(style.as_path(), "$brand: #0af;\n")?;
3494        let resolved = path_to_file_uri(style.as_path());
3495        let storage = fixture_cache_storage(style.as_path());
3496        clear_external_sif_memory_cache_for_storage_for_test(&storage);
3497        let original_sif =
3498            generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
3499                resolved.as_str(),
3500                &OmenaBridgeExternalSifCacheContextV0::default(),
3501                Some(&storage),
3502            )?;
3503        let cache_dir = external_sif_cache_dir_for_path(style.as_path(), Some(&storage))
3504            .ok_or_else(|| std::io::Error::other("cache dir"))?;
3505        let original_shard = serde_json::from_slice::<Value>(&fs::read(
3506            only_fixture_cache_shard_path(cache_dir.as_path())?,
3507        )?)?;
3508        let key = original_shard
3509            .get("key")
3510            .and_then(Value::as_str)
3511            .ok_or_else(|| std::io::Error::other("shard key"))?
3512            .to_string();
3513        let source_hash = original_shard
3514            .get("sourceHash")
3515            .and_then(Value::as_str)
3516            .ok_or_else(|| std::io::Error::other("source hash"))?
3517            .to_string();
3518        let resolved_base_dir = original_shard
3519            .get("resolvedBaseDir")
3520            .and_then(Value::as_str)
3521            .ok_or_else(|| std::io::Error::other("resolved base"))?
3522            .to_string();
3523        let mut poisoned_sif = original_sif.clone();
3524        poisoned_sif.exports.variables.clear();
3525        let poisoned_sif_json = write_omena_sif_json_v1(&poisoned_sif)?;
3526        let poisoned_payload_digest = compute_omena_sif_leaf_hash_v1(poisoned_sif_json.as_bytes());
3527        let poisoned_sif_hash = compute_omena_sif_artifact_hash_v1(&poisoned_sif)?;
3528        let verdict_dir = storage
3529            .recorded_verdict_dir()
3530            .ok_or_else(|| std::io::Error::other("verdict dir"))?
3531            .to_path_buf();
3532        Ok(RecordedVerdictAttackFixture {
3533            root,
3534            resolved,
3535            storage,
3536            verdict_dir,
3537            key,
3538            source_hash,
3539            resolved_base_dir,
3540            original_shard,
3541            original_sif,
3542            poisoned_sif,
3543            poisoned_sif_json,
3544            poisoned_payload_digest,
3545            poisoned_sif_hash,
3546        })
3547    }
3548
3549    fn write_poisoned_low_tier_fixture_shard(
3550        fixture: &RecordedVerdictAttackFixture,
3551    ) -> Result<(), Box<dyn std::error::Error>> {
3552        let mut poisoned = fixture.original_shard.clone();
3553        poisoned["sifJson"] = Value::String(fixture.poisoned_sif_json.clone());
3554        poisoned["payloadDigest"] =
3555            Value::String(fixture.poisoned_payload_digest.as_str().to_string());
3556        poisoned["trustEnvelope"] = serde_json::to_value(OmenaSifShardTrustEnvelopeV1 {
3557            schema_version: OMENA_SIF_SHARD_TRUST_ENVELOPE_SCHEMA_VERSION_V1.to_string(),
3558            product: OMENA_SIF_SHARD_TRUST_ENVELOPE_PRODUCT_V1.to_string(),
3559            trust_tier: OmenaSifTrustTierV1::T1,
3560            payload_digest: fixture.poisoned_payload_digest.clone(),
3561            signature: None,
3562            lock_binding: OmenaSifShardLockBindingV1 {
3563                canonical_url: fixture.resolved.clone(),
3564                sif_hash: fixture.poisoned_sif_hash.clone(),
3565            },
3566        })?;
3567        let cache_dir = fixture
3568            .storage
3569            .workspace_cache_root()
3570            .join(EXTERNAL_SIF_CACHE_DIR);
3571        let shard_path = only_fixture_cache_shard_path(cache_dir.as_path())?;
3572        fs::write(shard_path, write_omena_canonical_json_bytes_v1(&poisoned)?)?;
3573        Ok(())
3574    }
3575
3576    fn generate_fixture_sif_for_resolved_style_path(
3577        path: &Path,
3578        resolved_path: &str,
3579    ) -> Result<OmenaSifV1, String> {
3580        let cache_storage = fixture_cache_storage(path);
3581        generate_omena_bridge_sif_for_resolved_style_path_with_cache_context_and_storage(
3582            resolved_path,
3583            &OmenaBridgeExternalSifCacheContextV0::default(),
3584            Some(&cache_storage),
3585        )
3586    }
3587}