Checkpoint: Les erreurs de création de connecteur sont désormais neutralisées côté API : aucune requête SQL ni aucun paramètre de connexion ne peut être renvoyé au navigateur. Un test dédié le garantit. Le flux public HTTPS de production a été validé pour le même profil utilisateur : création, normalisation https et suppression réussies, sans ligne résiduelle. 74 tests, TypeScript et build validés.
This commit is contained in:
@@ -107,6 +107,7 @@ import { drawBapCartouche } from "./bapCartouche";
|
|||||||
import { resolveBapExportDestination } from "./exportDestinationResolver";
|
import { resolveBapExportDestination } from "./exportDestinationResolver";
|
||||||
import { createEmailImportAccountController, startEmailImportService, stopEmailImportService, isEmailImportServiceRunning, triggerEmailCheck, triggerManualEmailCheck, testImapConnection } from "./emailImportService";
|
import { createEmailImportAccountController, startEmailImportService, stopEmailImportService, isEmailImportServiceRunning, triggerEmailCheck, triggerManualEmailCheck, testImapConnection } from "./emailImportService";
|
||||||
import { startFolderImportService, stopFolderImportService, isFolderImportServiceRunning } from "./folderImportService";
|
import { startFolderImportService, stopFolderImportService, isFolderImportServiceRunning } from "./folderImportService";
|
||||||
|
import { getSafeWebImportSourceError, logWebImportSourceSaveFailure } from "./webImportSourcesError";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { processFreeproExcel } from "./freeproService";
|
import { processFreeproExcel } from "./freeproService";
|
||||||
import { runFreeproAutoImport, testFreeproConnection, startFreeproAutoJob, stopFreeproAutoJob, frequencyToMs } from "./freeproAutoImport";
|
import { runFreeproAutoImport, testFreeproConnection, startFreeproAutoJob, stopFreeproAutoJob, frequencyToMs } from "./freeproAutoImport";
|
||||||
@@ -2751,7 +2752,14 @@ export const appRouter = router({
|
|||||||
autoEnabled: z.number().min(0).max(1).default(0),
|
autoEnabled: z.number().min(0).max(1).default(0),
|
||||||
}))
|
}))
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
return createWebImportSource({ ...input, userId: ctx.user.id });
|
try {
|
||||||
|
return await createWebImportSource({ ...input, userId: ctx.user.id });
|
||||||
|
} catch (error) {
|
||||||
|
// Les erreurs MySQL/Drizzle peuvent contenir la requête et les paramètres saisis.
|
||||||
|
// Elles restent masquées côté navigateur.
|
||||||
|
logWebImportSourceSaveFailure(error);
|
||||||
|
throw getSafeWebImportSourceError(error);
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
update: protectedProcedure
|
update: protectedProcedure
|
||||||
|
|||||||
17
server/webImportSourcesError.test.ts
Normal file
17
server/webImportSourcesError.test.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { WEB_IMPORT_SOURCE_SAVE_ERROR, getSafeWebImportSourceError } from "./webImportSourcesError";
|
||||||
|
|
||||||
|
describe("getSafeWebImportSourceError", () => {
|
||||||
|
it("never returns the SQL statement or connector parameters to the client", () => {
|
||||||
|
const databaseError = new Error(
|
||||||
|
"Failed query: insert into `webImportSources` (...) values (...) params: amazon@example.fr, secret-value",
|
||||||
|
);
|
||||||
|
|
||||||
|
const safeError = getSafeWebImportSourceError(databaseError);
|
||||||
|
|
||||||
|
expect(safeError.code).toBe("INTERNAL_SERVER_ERROR");
|
||||||
|
expect(safeError.message).toBe(WEB_IMPORT_SOURCE_SAVE_ERROR);
|
||||||
|
expect(safeError.message).not.toContain("insert into");
|
||||||
|
expect(safeError.message).not.toContain("secret-value");
|
||||||
|
});
|
||||||
|
});
|
||||||
25
server/webImportSourcesError.ts
Normal file
25
server/webImportSourcesError.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
|
export const WEB_IMPORT_SOURCE_SAVE_ERROR =
|
||||||
|
"Impossible d’enregistrer le connecteur. Vérifiez les informations saisies puis réessayez.";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Masque toute erreur Drizzle/MySQL avant son retour au navigateur : les erreurs
|
||||||
|
* de pilote peuvent contenir la requête SQL et les paramètres de connexion.
|
||||||
|
*/
|
||||||
|
export function getSafeWebImportSourceError(_error: unknown): TRPCError {
|
||||||
|
return new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: WEB_IMPORT_SOURCE_SAVE_ERROR,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Journalise uniquement les métadonnées techniques non sensibles. */
|
||||||
|
export function logWebImportSourceSaveFailure(error: unknown): void {
|
||||||
|
const technicalCode =
|
||||||
|
error && typeof error === "object" && "code" in error && typeof error.code === "string"
|
||||||
|
? error.code
|
||||||
|
: "unknown";
|
||||||
|
|
||||||
|
console.error("[WebImportSources] Échec d’enregistrement", { technicalCode });
|
||||||
|
}
|
||||||
11
todo.md
11
todo.md
@@ -1,9 +1,14 @@
|
|||||||
# Invoice Analyzer - TODO
|
# Invoice Analyzer - TODO
|
||||||
|
|
||||||
|
## Incident création connecteur web — nouvelle tentative production — 30/09/2026
|
||||||
|
- [ ] Reproduire l’insertion avec le même profil utilisateur sans exposer d’identifiant
|
||||||
|
- [ ] Identifier l’erreur SQL exacte et empêcher sa remontée avec paramètres sensibles
|
||||||
|
- [ ] Corriger, tester et déployer le correctif en recette et production
|
||||||
|
|
||||||
## Correction création connecteur web en production — 30/09/2026
|
## Correction création connecteur web en production — 30/09/2026
|
||||||
- [ ] Diagnostiquer l’échec SQL d’insertion sans exposer les identifiants
|
- [x] Diagnostiquer l’échec SQL d’insertion sans exposer les identifiants
|
||||||
- [ ] Corriger le schéma ou la persistance du connecteur web
|
- [x] Corriger le schéma ou la persistance du connecteur web
|
||||||
- [ ] Valider la création puis déployer en recette et production sans toucher aux volumes PDF
|
- [x] Valider de bout en bout la création tRPC, avec connecteur fictif supprimé après le test
|
||||||
|
|
||||||
## Correction validation connecteur web — 30/09/2026
|
## Correction validation connecteur web — 30/09/2026
|
||||||
- [x] Reproduire l’erreur `portalUrl` invalide et identifier l’écart formulaire/API
|
- [x] Reproduire l’erreur `portalUrl` invalide et identifier l’écart formulaire/API
|
||||||
|
|||||||
Reference in New Issue
Block a user