@futdevpro/nts-dynamo 1.15.99 → 1.15.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dynamo/logs/cicd-pipeline/output.log +1820 -1748
- package/.dynamo/logs/cicd-pipeline/status.json +28 -28
- package/build/_collections/client-safe-error.util.d.ts +33 -0
- package/build/_collections/client-safe-error.util.d.ts.map +1 -0
- package/build/_collections/client-safe-error.util.js +68 -0
- package/build/_collections/client-safe-error.util.js.map +1 -0
- package/build/_models/control-models/endpoint-params.control-model.d.ts.map +1 -1
- package/build/_models/control-models/endpoint-params.control-model.js +6 -11
- package/build/_models/control-models/endpoint-params.control-model.js.map +1 -1
- package/build/_services/server/app.server.d.ts.map +1 -1
- package/build/_services/server/app.server.js +5 -1
- package/build/_services/server/app.server.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/_collections/client-safe-error.util.spec.ts +95 -0
- package/src/_collections/client-safe-error.util.ts +72 -0
- package/src/_models/control-models/endpoint-params.control-model.ts +6 -14
- package/src/_services/server/app.server.ts +5 -1
- package/src/index.ts +1 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { DyFM_EnvironmentFlag, DyFM_Error } from '@futdevpro/fsm-dynamo';
|
|
2
|
+
|
|
3
|
+
import { DyNTS_ClientSafeError_Util } from './client-safe-error.util';
|
|
4
|
+
import { DyNTS_global_settings } from './global-settings.const';
|
|
5
|
+
|
|
6
|
+
// A kliens-safe error-strip SSOT tesztjei (BFR-FDPAUTHSERVICE-002) — a 3 strip-szabály +
|
|
7
|
+
// a nem-DyFM fail-safe ág. A DyFM_Error mező-neveit a valós instansán verifikáljuk (no-guessing).
|
|
8
|
+
describe('| DyNTS_ClientSafeError_Util', (): void => {
|
|
9
|
+
|
|
10
|
+
/** Egy teljes-mezős DyFM_Error a strip-tesztekhez (a leak-vektor mezőkkel feltöltve). */
|
|
11
|
+
function buildRichError(): DyFM_Error {
|
|
12
|
+
const error: DyFM_Error = new DyFM_Error({
|
|
13
|
+
message: 'internal reason',
|
|
14
|
+
userMessage: 'user facing message',
|
|
15
|
+
errorCode: 'DyNTS-TEST-CSE0',
|
|
16
|
+
error: new Error('inner cause'),
|
|
17
|
+
additionalContent: { userId: 'u1' },
|
|
18
|
+
confidentialContent: { secret: 's3cr3t' },
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return error;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
afterEach((): void => {
|
|
25
|
+
// a prod-teszt env-átállítását visszaállítjuk (a global-settings megosztott singleton)
|
|
26
|
+
DyNTS_global_settings.env_settings.environment = DyFM_EnvironmentFlag.local;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('| MINDIG strip-eli a leak-vektor mezőket (nem-prod env-ben is)', (): void => {
|
|
30
|
+
DyNTS_global_settings.env_settings.environment = DyFM_EnvironmentFlag.local;
|
|
31
|
+
const error: DyFM_Error = buildRichError();
|
|
32
|
+
|
|
33
|
+
const safe: Record<string, unknown> = DyNTS_ClientSafeError_Util.toClientSafe(error) as Record<string, unknown>;
|
|
34
|
+
|
|
35
|
+
expect(safe['__localStack']).toBeUndefined();
|
|
36
|
+
expect(safe['stack']).toBeUndefined();
|
|
37
|
+
expect(safe['___systemVersion']).toBeUndefined();
|
|
38
|
+
expect(safe['___issuer']).toBeUndefined();
|
|
39
|
+
expect(safe['___issuerSystem']).toBeUndefined();
|
|
40
|
+
expect(safe['___issuerService']).toBeUndefined();
|
|
41
|
+
expect(safe['confidentialContent']).toBeUndefined();
|
|
42
|
+
expect(safe['error']).toBeUndefined();
|
|
43
|
+
expect(safe['errors']).toBeUndefined();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('| MEGTARTJA a kliens-fogyasztott mezőket (_message, _errorCodes, __userMessage)', (): void => {
|
|
47
|
+
DyNTS_global_settings.env_settings.environment = DyFM_EnvironmentFlag.local;
|
|
48
|
+
const error: DyFM_Error = buildRichError();
|
|
49
|
+
|
|
50
|
+
const safe: Record<string, unknown> = DyNTS_ClientSafeError_Util.toClientSafe(error) as Record<string, unknown>;
|
|
51
|
+
|
|
52
|
+
expect(safe['_message']).toBe('internal reason');
|
|
53
|
+
expect(safe['__userMessage']).toBe('user facing message');
|
|
54
|
+
expect(Array.isArray(safe['_errorCodes'])).toBeTrue();
|
|
55
|
+
expect((safe['_errorCodes'] as string[]).join(',')).toContain('DyNTS-TEST-CSE0');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('| nem-prod env: az additionalContent (diagnosztikai snapshot) MARAD', (): void => {
|
|
59
|
+
DyNTS_global_settings.env_settings.environment = DyFM_EnvironmentFlag.local;
|
|
60
|
+
const error: DyFM_Error = buildRichError();
|
|
61
|
+
|
|
62
|
+
const safe: Record<string, unknown> = DyNTS_ClientSafeError_Util.toClientSafe(error) as Record<string, unknown>;
|
|
63
|
+
|
|
64
|
+
expect(safe['additionalContent']).toEqual({ userId: 'u1' });
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('| prod env: az additionalContent IS strip-elve', (): void => {
|
|
68
|
+
DyNTS_global_settings.env_settings.environment = DyFM_EnvironmentFlag.prod;
|
|
69
|
+
const error: DyFM_Error = buildRichError();
|
|
70
|
+
|
|
71
|
+
const safe: Record<string, unknown> = DyNTS_ClientSafeError_Util.toClientSafe(error) as Record<string, unknown>;
|
|
72
|
+
|
|
73
|
+
expect(safe['additionalContent']).toBeUndefined();
|
|
74
|
+
// a mindig-strip lista prod-ban is érvényes
|
|
75
|
+
expect(safe['__localStack']).toBeUndefined();
|
|
76
|
+
expect(safe['stack']).toBeUndefined();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('| nem-DyFM object-error: fail-safe minimál { message } shape (axios-config nem szivárog)', (): void => {
|
|
80
|
+
const axiosLike: { message: string; config: { headers: { authorization: string } } } = {
|
|
81
|
+
message: 'Request failed with status code 401',
|
|
82
|
+
config: { headers: { authorization: 'Bearer LEAK' } },
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const safe: Record<string, unknown> = DyNTS_ClientSafeError_Util.toClientSafe(axiosLike) as Record<string, unknown>;
|
|
86
|
+
|
|
87
|
+
expect(safe['message']).toBe('Request failed with status code 401');
|
|
88
|
+
expect(safe['config']).toBeUndefined();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('| nem-object error (string/undefined): változatlanul átmegy', (): void => {
|
|
92
|
+
expect(DyNTS_ClientSafeError_Util.toClientSafe('plain string')).toBe('plain string');
|
|
93
|
+
expect(DyNTS_ClientSafeError_Util.toClientSafe(undefined)).toBeUndefined();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { DyFM_EnvironmentFlag, DyFM_Error } from '@futdevpro/fsm-dynamo';
|
|
2
|
+
|
|
3
|
+
import { DyNTS_global_settings } from './global-settings.const';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Kliens-safe error-body SSOT (BFR-FDPAUTHSERVICE-002 — error-response info-disclosure fix).
|
|
7
|
+
*
|
|
8
|
+
* A DyFM_Error HTTP-response-ba szerializálásakor a belső diagnosztikai mezők NEM mehetnek ki a
|
|
9
|
+
* kliensre — env-FÜGGETLENÜL (a korábbi prod-only strip a test/dev környezetben TELJES leaket
|
|
10
|
+
* hagyott: `__localStack` stack-trace, `___systemVersion`, issuer-lánc). A strip-szabályok:
|
|
11
|
+
*
|
|
12
|
+
* 1. MINDIG ki (env-független, kliens sehol nem fogyasztja → 0 regresszió):
|
|
13
|
+
* `__localStack`, `stack`, `___systemVersion`, `___issuer`, `___issuerSystem`,
|
|
14
|
+
* `___issuerService`, `confidentialContent`, `error`, `errors` (a nyers belső error-lánc
|
|
15
|
+
* stack-eket hordoz).
|
|
16
|
+
* 2. CSAK prod-ban ki: `additionalContent` — test/dev környezetben a diagnosztikai state-snapshot
|
|
17
|
+
* szándékosan kimegy (a hibafelület-debug erre épül), prod-ban nem.
|
|
18
|
+
* 3. MARAD (kliens-kompatibilitás — a FDP-kliens error-interceptorok fogyasztják):
|
|
19
|
+
* `_message`, `_errorCodes`, `__userMessage`, `___status`, `level`.
|
|
20
|
+
*
|
|
21
|
+
* NEM-DyFM error (plain Error / axios-error / bármi más): minimál `{ message }` shape — a plain
|
|
22
|
+
* Error eddig is üres `{}`-ként szerializálódott (non-enumerable mezők), az axios-jellegű error-ok
|
|
23
|
+
* viszont enumerable config/request mezőkben auth-headert is szivárogtathatnak → fail-safe minimál.
|
|
24
|
+
*
|
|
25
|
+
* ⚠️ A metódus MUTÁLJA a kapott DyFM_Error-t (a meglévő endpoint-strip precedensét követve) —
|
|
26
|
+
* a hívási pont a response-küldés, ahol az error-objektum életciklusa véget ér.
|
|
27
|
+
*/
|
|
28
|
+
export class DyNTS_ClientSafeError_Util {
|
|
29
|
+
|
|
30
|
+
/** A MINDIG strip-elendő DyFM-mezők (env-független — kliens-fogyasztás nélküli leak-vektorok). */
|
|
31
|
+
private static readonly alwaysStripKeys: string[] = [
|
|
32
|
+
'__localStack',
|
|
33
|
+
'stack',
|
|
34
|
+
'___systemVersion',
|
|
35
|
+
'___issuer',
|
|
36
|
+
'___issuerSystem',
|
|
37
|
+
'___issuerService',
|
|
38
|
+
'confidentialContent',
|
|
39
|
+
'error',
|
|
40
|
+
'errors',
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Az error kliens-safe formára hozása a HTTP-response-küldés előtt.
|
|
45
|
+
* DyFM_Error: in-place strip (1-3. szabályok szerint); nem-DyFM object: minimál `{ message }`.
|
|
46
|
+
*/
|
|
47
|
+
static toClientSafe(error: unknown): unknown {
|
|
48
|
+
// DyFM_Error: célzott mező-strip, a kliens-fogyasztott mezők megtartásával
|
|
49
|
+
if (DyFM_Error.isDyFMError(error)) {
|
|
50
|
+
const dyError: Record<string, unknown> = error as unknown as Record<string, unknown>;
|
|
51
|
+
|
|
52
|
+
for (const key of DyNTS_ClientSafeError_Util.alwaysStripKeys) {
|
|
53
|
+
delete dyError[key];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// prod: a diagnosztikai state-snapshot sem megy ki; test/dev: szándékosan marad (debug-UX)
|
|
57
|
+
if (DyNTS_global_settings.env_settings.environment === DyFM_EnvironmentFlag.prod) {
|
|
58
|
+
delete dyError['additionalContent'];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return error;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// nem-DyFM object-error (plain/axios/egyéb): fail-safe minimál shape — a nyers objektum
|
|
65
|
+
// enumerable mezői (pl. axios config.headers) szivárogtathatnak
|
|
66
|
+
if (error && typeof error === 'object') {
|
|
67
|
+
return { message: (error as Error).message ?? 'Internal server error' };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from '@futdevpro/fsm-dynamo';
|
|
8
8
|
|
|
9
9
|
import { DyNTS_global_settings } from '../../_collections/global-settings.const';
|
|
10
|
+
import { DyNTS_ClientSafeError_Util } from '../../_collections/client-safe-error.util';
|
|
10
11
|
import { DyNTS_RouteSecurity } from '../../_enums/route-security.enum';
|
|
11
12
|
import { DyNTS_GlobalService } from '../../_services/core/global.service';
|
|
12
13
|
|
|
@@ -487,20 +488,11 @@ export class DyNTS_Endpoint_Params<
|
|
|
487
488
|
res.status(500);
|
|
488
489
|
}
|
|
489
490
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
delete (error as DyFM_Error).additionalContent;
|
|
496
|
-
delete (error as DyFM_Error).error;
|
|
497
|
-
delete (error as DyFM_Error).errors;
|
|
498
|
-
delete (error as DyFM_Error).stack;
|
|
499
|
-
delete (error as DyFM_Error).__localStack;
|
|
500
|
-
/* DyFM_Error.deleteErrorContent(error as DyFM_Error); */
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
res.send(error);
|
|
491
|
+
// Kliens-safe strip ENV-FÜGGETLENÜL (BFR-FDPAUTHSERVICE-002): a korábbi prod-only blokk a
|
|
492
|
+
// test/dev környezetben TELJES DyFM-leaket hagyott (__localStack + ___systemVersion + issuer).
|
|
493
|
+
// A szabályok (mindig-strip / prod-only additionalContent / kliens-mezők megtartása) SSOT-ja
|
|
494
|
+
// a DyNTS_ClientSafeError_Util.
|
|
495
|
+
res.send(DyNTS_ClientSafeError_Util.toClientSafe(error));
|
|
504
496
|
|
|
505
497
|
if (this.logRequest) {
|
|
506
498
|
if (this.logResponseContent) {
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
import { DyNTS_defaultFallbackCacheMaxAge } from '../../_collections/default-fallback-cache-max-age.const';
|
|
31
31
|
import { DyNTS_defaultNotFoundPageHtml } from '../../_collections/default-not-found-page.const';
|
|
32
32
|
import { DyNTS_global_settings } from '../../_collections/global-settings.const';
|
|
33
|
+
import { DyNTS_ClientSafeError_Util } from '../../_collections/client-safe-error.util';
|
|
33
34
|
import { DyNTS_getEnvironment_settings } from '../../_collections/get-environment-settings.util';
|
|
34
35
|
import { DyNTS_createSecurityHeadersMiddleware } from '../../_collections/security-headers.util';
|
|
35
36
|
import { DyNTS_SecurityHeaders_Settings } from '../../_models/interfaces/security-headers-settings.interface';
|
|
@@ -1482,7 +1483,10 @@ export abstract class DyNTS_App extends DyNTS_SingletonService {
|
|
|
1482
1483
|
|
|
1483
1484
|
await DyNTS_GlobalService.globalErrorHandler?.(d_error, req, res);
|
|
1484
1485
|
|
|
1485
|
-
|
|
1486
|
+
// Kliens-safe strip (BFR-FDPAUTHSERVICE-002): az express-error út eddig NYERS error-t küldött
|
|
1487
|
+
// (0 strip) — DyFM-nél belső stack/verzió/issuer, axios-jellegű error-nál akár auth-header is
|
|
1488
|
+
// kimehetett. A strip-szabályok SSOT-ja a DyNTS_ClientSafeError_Util.
|
|
1489
|
+
res.send(DyNTS_ClientSafeError_Util.toClientSafe(error))
|
|
1486
1490
|
} else {
|
|
1487
1491
|
DyFM_Log.H_error(
|
|
1488
1492
|
'WTF??? express error; without error?...' +
|
package/src/index.ts
CHANGED
|
@@ -54,6 +54,7 @@ export * from './_models/interfaces/cors-settings.interface';
|
|
|
54
54
|
export * from './_models/interfaces/security-headers-settings.interface';
|
|
55
55
|
export * from './_models/interfaces/db-query-options.interface';
|
|
56
56
|
export * from './_collections/security-headers.util';
|
|
57
|
+
export * from './_collections/client-safe-error.util';
|
|
57
58
|
|
|
58
59
|
// models/CONTROL MODELS
|
|
59
60
|
export * from './_models/control-models/api-call-params.control-model';
|