tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1532Shell 196Rust 108Markdown 993686 Definitions

⎇ 84e3737e10e17927380ed3e232e50cebfdd39123 crates/takana-build/tests/hidratacion_no_escapa.rs Rust · 76 lines · 3.5 KB History Who touched what raw

takana etapa 5d: comentarios de crates, CLAUDE.md y el skill de granja b818f5249 · Sergio ·

1//! La hidratación no escribe fuera de su root, aunque el FHS ya traiga un symlink que salga.
2//!
3//! EL FALLO (medido 2026-08-30, antes de `no_escapa`): hidratar es `target_fhs.join(rel)` y
4//! escribir por ruta. Un artefacto que trae `usr/share/pkg → /algún/lado` deja ese symlink puesto
5//! —los symlinks se replican literales, y así debe ser— y la hidratación SIGUIENTE escribía
6//! `usr/share/pkg/archivo` **fuera del root**, devolviendo `Ok(1)`. Con `takana hydrate --into`
7//! sobre una imagen, eso es escribir en el sistema anfitrión diciendo que todo fue bien.
8//!
9//! El corpus no lo disparaba (de 160 symlinks absolutos ninguno apunta a un directorio, y de
10//! ~42 000 relativos ninguno sale de su artefacto), así que esto es una mina desactivada, no un
11//! incendio apagado. Por eso el test: lo que no dispara nadie tampoco lo nota nadie.
12
13use std::path::Path;
14
15use takana_build::hydrate::hydrate;
16use takana_core::LinkMode;
17
18/// Artefacto que trae `usr/share/pkg` como symlink a `destino`.
19fn artefacto_con_symlink(dir: &Path, destino: &Path) {
20    std::fs::create_dir_all(dir.join("usr/share")).unwrap();
21    std::os::unix::fs::symlink(destino, dir.join("usr/share/pkg")).unwrap();
22}
23
24/// Artefacto que trae un fichero DEBAJO de esa misma ruta.
25fn artefacto_con_fichero(dir: &Path) {
26    std::fs::create_dir_all(dir.join("usr/share/pkg")).unwrap();
27    std::fs::write(dir.join("usr/share/pkg/archivo"), b"contenido").unwrap();
28}
29
30#[test]
31fn un_symlink_del_fhs_que_sale_del_root_no_deja_escribir() {
32    let fuera = tempfile::tempdir().unwrap();
33    let fhs = tempfile::tempdir().unwrap();
34    let a = tempfile::tempdir().unwrap();
35    let b = tempfile::tempdir().unwrap();
36
37    artefacto_con_symlink(a.path(), fuera.path());
38    hydrate(a.path(), fhs.path(), LinkMode::Static, None).unwrap();
39
40    artefacto_con_fichero(b.path());
41    let r = hydrate(b.path(), fhs.path(), LinkMode::Static, None);
42
43    assert!(r.is_err(), "hidratar a través del symlink tiene que FALLAR, no devolver Ok");
44    let msg = format!("{}", r.unwrap_err());
45    assert!(msg.contains("se saldría del root"), "el error tiene que decir qué pasó: {msg}");
46    assert!(
47        !fuera.path().join("archivo").exists(),
48        "ESCAPÓ: el fichero se escribió fuera del target_fhs"
49    );
50}
51
52/// El contrapeso: un symlink de directorio que se queda DENTRO tiene que seguir funcionando.
53/// Es el caso de usr-merge (`/lib → usr/lib`) y el de los temas de iconos (`22@2x → 22`), o sea
54/// la mayoría de los symlinks de directorio que hay en el corpus. Un guardián que los rompiera
55/// sería peor que el agujero que tapa.
56#[test]
57fn un_symlink_que_se_queda_dentro_sigue_hidratando() {
58    let fhs = tempfile::tempdir().unwrap();
59    let a = tempfile::tempdir().unwrap();
60    let b = tempfile::tempdir().unwrap();
61
62    // `usr/share/pkg → ../otro` (relativo y dentro), con el destino existiendo.
63    std::fs::create_dir_all(a.path().join("usr/otro")).unwrap();
64    std::fs::write(a.path().join("usr/otro/.marca"), b"x").unwrap();
65    std::fs::create_dir_all(a.path().join("usr/share")).unwrap();
66    std::os::unix::fs::symlink("../otro", a.path().join("usr/share/pkg")).unwrap();
67    hydrate(a.path(), fhs.path(), LinkMode::Static, None).unwrap();
68
69    artefacto_con_fichero(b.path());
70    let r = hydrate(b.path(), fhs.path(), LinkMode::Static, None);
71    assert!(r.is_ok(), "un symlink que no sale del root no puede bloquear la hidratación: {r:?}");
72    assert!(
73        fhs.path().join("usr/otro/archivo").exists(),
74        "el fichero tiene que aterrizar donde apunta el symlink, dentro del root"
75    );
76}