| Metadato | Valor |
|---|---|
| Versión / Version | v1 |
| Estado / Status | Vigente desde la fase 06.5 para todo AIP nuevo creado por POST /api/v1/ingest/file |
| URI del perfil / Profile URI | https://docs.docuten.com/earchiving/filename-policy/v1 |
| Dónde se cita / Where it is cited | El nombre resultante es el aipPath de cada fichero dentro de premis.xml, de las hojas del sello #1 y de la atestación; el nombre declarado (originalNameNfc) se sella como originalName |
| Implementación / Implementation | FilenamePolicy (derive, collisionKey), FileEntry, SipBuilderService, en earchiving-ingest/.../ingest/service/ |
| Decisiones / Decisions | D-07, D-08, D-09 (06.5-CONTEXT.md) |
| Fecha / Date | 2026-11-02 |
Este documento define la regla cerrada y versionada con la que /file convierte el nombre que
declara el cliente en el nombre que lleva el fichero dentro del paquete. commons-ip2 2.11.2 no sanea
IPFile.setRenameTo (un nombre ../evil.pdf produce una entrada ZIP fuera de data/), así que la
seguridad del nombre es nuestra: FilenamePolicy es la única fuente del nombre que llega al CSIP.
La URI es un identificador, no una URL que deba resolverse.
Cambiar esta regla es una versión nueva (v2), nunca una edición:
aipPathqueda sellado enpremis.xml, en las hojas del sello #1 y en la atestación.
This document defines the closed, versioned rule that turns the client-declared file name into the
name stored in the package. commons-ip2 does not sanitise setRenameTo, so the policy is the only
source of the name that reaches the CSIP. Changing the rule is a new version (v2), never an edit.
La política conserva tal cual un nombre seguro en NFC (Unicode, espacios y ñ incluidos) y solo
deriva lo inseguro. Rechaza únicamente lo irrecuperable (§4). originalNameNfc es el último
segmento del nombre declarado, en NFC: la parte de directorio que algunos clientes envían no es un
nombre de fichero y no se sella. Es null si el cliente no declaró nombre.
Safe NFC names are kept as they are; only unsafe parts are derived; only the unrecoverable is
rejected. originalNameNfc is the last path segment of the declared name in NFC, or null.
Las reglas se aplican en este orden. {i} es la posición del fichero en el lote.
| ID | Regla / Rule | Entrada | Salida |
|---|---|---|---|
| FN-R01 | Sin nombre declarado (null o en blanco) → original-{i}; originalNameNfc = null |
|
original-0 |
| FN-R02 | Se toma el último segmento tras la última / o \, en NFC y sin espacios de los extremos; ese segmento es originalNameNfc. Si queda vacío → FN-R01 |
C:\Users\x\factura.pdf |
factura.pdf |
| FN-R03 | Rechazo de lo irrecuperable (§4), sobre TODO el nombre declarado | a\u0007b.pdf |
rechazado |
| FN-R04 | Segmento . o .. → original-{i} (originalNameNfc conserva el declarado) |
.. |
original-0 |
| FN-R05 | Cada carácter de < > : " \| ? * se sustituye por _ (originalNameNfc los conserva) |
a:b*c?.pdf |
a_b_c_.pdf |
| FN-R06 | Se quitan puntos y espacios finales; si queda vacío → fallback FN-R09 | a.pdf. |
a.pdf |
| FN-R07 | Nombre reservado de Windows (parte anterior al primer . igual, sin distinguir mayúsculas, a CON, PRN, AUX, NUL, COM1–COM9, LPT1–LPT9) → se antepone _ |
con.pdf |
_con.pdf |
| FN-R08 | Si el nombre excede 255 bytes UTF-8: se trunca el stem en frontera de code point preservando la extensión (desde la última ., máximo 16 bytes); si la extensión sola no cabe o deja el stem vacío → fallback FN-R09 |
nombre de 300 bytes .pdf |
≤ 255 bytes, termina en .pdf |
| FN-R09 | Fallback: original-{i} más . y la extensión del declarado solo si casa con [A-Za-z0-9]{1,16}; sin extensión en otro caso |
... |
original-0 |
Ejemplos que se conservan tal cual / kept as they are: Factura Ñ ñ.pdf, a+b c.pdf, 100%.pdf.
Un nombre en NFD (résumé.pdf) se devuelve en NFC.
Efecto de FN-R02 sobre rutas hostiles / Hostile paths: ../evil.pdf → evil.pdf; a\b.pdf → b.pdf.
Un nombre declarado se rechaza (no se falsifica) si:
| Motivo | Condición |
|---|---|
CONTROL_CHARACTERS |
contiene un carácter de control Cc (U+0000–U+001F, U+007F–U+009F), un carácter de formato Cf (entre otros, los de dirección bidi U+202A–U+202E y U+2066–U+2069, U+200E/U+200F, U+200B–U+200D, U+FEFF, U+00AD) o un separador de línea o párrafo Zl/Zp (U+2028, U+2029) |
INVALID_UNICODE |
contiene un surrogate desparejado, U+FFFE o U+FFFF (no representables en XML 1.0) |
DECLARED_NAME_TOO_LONG |
el nombre declarado excede 1024 bytes UTF-8 |
Los caracteres Cf y Zl/Zp se rechazan porque son invisibles o reordenan lo que se muestra: un nombre como
Factura\u202Efdp.exe se vería como Facturaexe.pdf en el sello, en originalName y en el catálogo, y U+2028/U+2029
parten líneas en los visores de logs. Consecuencia asumida: U+200C/U+200D (ZWNJ/ZWJ), necesarios en algunas
escrituras (p. ej. persa o secuencias emoji), también se rechazan; los signos combinantes (Mn/Mc) y las letras
acentuadas siguen admitidos. La regla se cerró en v1 antes de que existiera ninguna instancia sellada.
A name with characters not representable in XML 1.0 is not falsified in PREMIS: it is rejected. So are format characters (Cf: bidi overrides/isolates, zero-width characters, BOM, soft hyphen) and line/paragraph separators (Zl, Zp): they spoof the displayed name or split log lines.
FilenamePolicy.collisionKey(finalName) = NFC y después toUpperCase(Locale.ROOT).toLowerCase(Locale.ROOT)
(casefold aproximado, D-09). Dos ficheros del mismo lote cuyas claves coinciden colisionan. Límite
conocido: ß/ẞ y la sigma final no son un casefold Unicode completo; se acepta como supuesto de baja
probabilidad (no se añade ICU4J).
La versión de la regla es v1 (FilenamePolicy.RULE_VERSION) y un test (FilenamePolicyTest) fija la
paridad entre este documento y el código: IDs FN-R01..FN-R09, versión y URI. Cualquier cambio de
comportamiento es una versión nueva (v2, URI nueva), nunca una edición de esta.