Constat de T6 (question 2) : la trace masquait un token recopié dans reason ou planId, mais les lignes [Optimiseur] (planId, texte, détail des refus, nom du handshake) et GetOptimizerStatus.name le portaient en clair. - OptimizerSecret : UNE fonction de masquage (texte brut et échappé JSON, objet JSON à toute profondeur, clés comprises ; ligne de trace sérialisée), partagée par le client et les traces. - OptimizerClient masque chaque message reçu dès son analyse, avec le token envoyé sur cette connexion : rien en aval (journal, traces, app) ne voit le secret ; dit une fois. - Test testAnEchoedTokenLeaksNowhere : secret avec " et \ recopié partout ; aucune ligne de journal (tous niveaux), aucune trace, aucune réponse RPC. 4 contre-épreuves mordent. decisions sans endpoint : IDENTIQUE (23 792 lignes), 0 rejeu. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
57 lines
2.7 KiB
C++
57 lines
2.7 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// Copyright (C) 2025 - 2026, Patrick Schurig / ETM PowerSync
|
|
#pragma once
|
|
|
|
#include <QByteArray>
|
|
#include <QJsonObject>
|
|
#include <QString>
|
|
|
|
/*!
|
|
* \file optimizersecret.h
|
|
* \brief Masquage du token d'optimiseur (brief 4 T7, correctif A) — LA fonction partagée par le
|
|
* journal, les traces et tout ce qui part vers l'app.
|
|
*
|
|
* Le token ne voyage que dans le handshake (§3). Un optimiseur qui le RECOPIE dans ce qu'il
|
|
* renvoie (\c reason, \c planId, un code, un paramètre, son \c name…) ferait sortir le secret par
|
|
* chacun des chemins qui relaient son texte : journal \c [Optimiseur], traces, \c GetOptimizerStatus.
|
|
* Plutôt que de masquer à chacun de ces chemins — et d'oublier le prochain —, le client masque
|
|
* chaque message REÇU dès son analyse (\c OptimizerClient), avant tout usage : rien en aval ne voit
|
|
* jamais le secret. L'écrivain des traces masque en plus la ligne sérialisée (défense en
|
|
* profondeur), avec la même fonction.
|
|
*
|
|
* \invariant Aucune fonction d'ici ne journalise, ni le token, ni sa longueur.
|
|
*/
|
|
namespace OptimizerSecret {
|
|
|
|
//! Ce qui remplace le token, partout.
|
|
constexpr const char *Masque = "[token masqué]";
|
|
|
|
/*!
|
|
* \brief Remplace dans un TEXTE chaque occurrence du token, sous sa forme brute et sous sa forme
|
|
* échappée JSON (\c " et \c \\ précédés d'un \c \\).
|
|
* \param texte Texte à masquer. \param token Le secret ; vide = rien à faire.
|
|
* \param[in,out] masques Incrémenté du nombre de remplacements (peut être \c nullptr).
|
|
* \return Le texte masqué.
|
|
*/
|
|
QString masquerTexte(const QString &texte, const QString &token, int *masques = nullptr);
|
|
|
|
/*!
|
|
* \brief Masque un message JSON reçu : chaque chaîne ET chaque clé d'objet, à toute profondeur.
|
|
* \param message Objet analysé. \param token Le secret ; vide = rien à faire.
|
|
* \param[in,out] masques Incrémenté du nombre de remplacements (peut être \c nullptr).
|
|
* \return L'objet masqué. Les nombres, booléens et la structure sont inchangés.
|
|
*/
|
|
QJsonObject masquerJson(const QJsonObject &message, const QString &token, int *masques = nullptr);
|
|
|
|
/*!
|
|
* \brief Masque une ligne JSON DÉJÀ SÉRIALISÉE (\c QJsonDocument, compacte) : le token y est cherché
|
|
* sous sa seule forme échappée — c'est ainsi, et seulement ainsi, qu'il y figurerait. Chercher la
|
|
* forme brute pourrait mordre sur la syntaxe de la ligne et la rendre illisible.
|
|
* \param ligne Ligne JSON. \param token Le secret, brut ; vide = rien à faire.
|
|
* \param[in,out] masques Incrémenté du nombre de remplacements (peut être \c nullptr).
|
|
* \return La ligne masquée.
|
|
*/
|
|
QByteArray masquerLigneJson(const QByteArray &ligne, const QByteArray &token, int *masques = nullptr);
|
|
|
|
}
|