Skip to content

Protocole d'assets ​

Les assets importés par l'utilisateur (images d'alerte, GIF) vivent dans userData/assets/ et sont référencés par un id d'asset court et opaque, jamais par un chemin de fichier. Deux canaux de livraison servent les mêmes fichiers : un protocole Electron privilégié pour les surfaces internes, et une route HTTP pour les sources navigateur OBS. Cette page s'adresse aux développeurs d'overlays custom.

Les assets sont gérés dans la Médiathèque ; le AlertDisplay.assetId d'un flux est résolu vers un id que l'overlay peut charger (voir Spécification des flux).

Format d'id d'asset ​

[a-zA-Z0-9_-]{8,16}

Les deux canaux de livraison appliquent cette regex exacte avant tout accès disque. Un id hors de cette plage est rejeté avec 404 et n'atteint jamais la résolution de fichier. C'est la première ligne de défense contre le path traversal.

Canal 1 - protocole streamoos-asset:// ​

Enregistré dans app/src/main/main.ts avant app.whenReady() via registerSchemesAsPrivileged :

typescript
protocol.registerSchemesAsPrivileged([
  {
    scheme: "streamoos-asset",
    privileges: { secure: true, standard: true, supportFetchAPI: true, corsEnabled: true },
  },
]);

Le handler lit l'id depuis le hostname de l'URL (streamoos-asset://<id>), valide la regex d'id, résout le chemin via prefsStore.getAssetFilePath(id), puis revérifie le containment (le chemin résolu doit rester dans userData/assets/) avant de streamer le fichier avec net.fetch("file://…"). Une tentative de traversal est loguée et retourne 403 ; un asset absent retourne 404.

streamoos-asset://aB3dEf9k

Ce schéma est autorisé dans la directive img-src de la Content-Security-Policy du renderer, donc le dashboard et les surfaces d'overlay internes peuvent l'utiliser directement dans un <img src>.

Canal 2 - route HTTP sur le serveur d'overlays ​

Les sources navigateur OBS ne peuvent pas utiliser un schéma Electron custom, donc le serveur HTTP d'overlays intégré (app/src/main/services/overlay-server.ts, 127.0.0.1:8766) expose les mêmes assets en HTTP simple :

http://127.0.0.1:8766/overlays/asset/<id>

La route applique la même regex d'id ([a-zA-Z0-9_-]{8,16}), résout le chemin via le callback injecté resolveCustomAssetPath(id), puis applique le containment contre les racines d'assets autorisées avant de servir. Sans resolver configuré, la route renvoie 404. Le serveur bloque aussi tout pathname contenant .. et valide chaque chemin résolu contre ses racines autorisées.

Les réponses partent avec Access-Control-Allow-Origin: * (pour OBS) et des en-têtes no-cache agressifs, donc un rechargement récupère toujours le dernier fichier.

Résolution depuis un flux ​

Quand un affichage de flux porte un assetId, resolveDisplayImage() (app/src/main/alerts/resolveDisplayImage.ts) retrouve l'asset et retourne le nom de fichier (ex. mon-gif.gif), qu'il écrit sur AlertDisplay.imageUrl. Le resolveAssetPath() de l'overlay retire les segments de chemin en tête et reconstruit un chemin relatif sous son propre répertoire d'assets. C'est le nom de fichier, pas l'id, que l'overlay charge finalement. imageUrl est calculé au déclenchement et n'est jamais persisté.

Synthèse sécurité ​

Gardestreamoos-asset:///overlays/asset/<id>
Regex d'id {8,16}ouioui
Containment de chemindans userData/assets/dans les racines autorisées
Mode d'échec403 traversal / 404404
Cachen/adésactivé (no-store)

Les deux canaux partagent une règle : un id est opaque, validé, et résolu côté serveur. Un overlay custom ne construit jamais lui-même un chemin de fichier.

Released under the MIT License.