@futdevpro/nts-dynamo 1.15.135 → 1.15.136
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 +1588 -1688
- package/.dynamo/logs/cicd-pipeline/status.json +29 -29
- package/__documentations/2026-08-14-field-encryption-mongoose-document-spread.md +124 -0
- package/build/_collections/field-encryption.util.d.ts +34 -0
- package/build/_collections/field-encryption.util.d.ts.map +1 -1
- package/build/_collections/field-encryption.util.js +46 -1
- package/build/_collections/field-encryption.util.js.map +1 -1
- package/package.json +3 -3
- package/src/_collections/field-encryption.util.spec.ts +86 -0
- package/src/_collections/field-encryption.util.ts +50 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"pid":
|
|
3
|
-
"startedAt": "2026-08-
|
|
4
|
-
"startedAtMs":
|
|
2
|
+
"pid": 10907,
|
|
3
|
+
"startedAt": "2026-08-14T03:44:08.561Z",
|
|
4
|
+
"startedAtMs": 1786679048561,
|
|
5
5
|
"phase": "npm-publish",
|
|
6
6
|
"pipelineComplete": false,
|
|
7
7
|
"serverRunning": false,
|
|
@@ -10,63 +10,63 @@
|
|
|
10
10
|
"steps": {
|
|
11
11
|
"validate-secrets": {
|
|
12
12
|
"status": "ok",
|
|
13
|
-
"startedAt": "2026-08-
|
|
13
|
+
"startedAt": "2026-08-14T03:44:08.862Z",
|
|
14
14
|
"logLineStart": 2,
|
|
15
15
|
"logLineEnd": 8,
|
|
16
|
-
"durationSec":
|
|
16
|
+
"durationSec": 0.3
|
|
17
17
|
},
|
|
18
18
|
"discord-start": {
|
|
19
19
|
"status": "ok",
|
|
20
|
-
"startedAt": "2026-08-
|
|
20
|
+
"startedAt": "2026-08-14T03:44:10.157Z",
|
|
21
21
|
"logLineStart": 9,
|
|
22
22
|
"logLineEnd": 14,
|
|
23
|
-
"durationSec": 5
|
|
23
|
+
"durationSec": 3.5
|
|
24
24
|
},
|
|
25
25
|
"pre-flight": {
|
|
26
26
|
"status": "ok",
|
|
27
|
-
"startedAt": "2026-08-
|
|
27
|
+
"startedAt": "2026-08-14T03:44:14.662Z",
|
|
28
28
|
"logLineStart": 15,
|
|
29
29
|
"logLineEnd": 17,
|
|
30
|
-
"durationSec": 2
|
|
30
|
+
"durationSec": 0.2
|
|
31
31
|
},
|
|
32
32
|
"install": {
|
|
33
33
|
"status": "ok",
|
|
34
|
-
"startedAt": "2026-08-
|
|
34
|
+
"startedAt": "2026-08-14T03:44:15.872Z",
|
|
35
35
|
"logLineStart": 18,
|
|
36
|
-
"logLineEnd":
|
|
37
|
-
"durationSec":
|
|
36
|
+
"logLineEnd": 102,
|
|
37
|
+
"durationSec": 29.3
|
|
38
38
|
},
|
|
39
39
|
"check-dev-leftovers": {
|
|
40
40
|
"status": "ok",
|
|
41
|
-
"startedAt": "2026-08-
|
|
42
|
-
"logLineStart":
|
|
43
|
-
"logLineEnd":
|
|
44
|
-
"durationSec":
|
|
41
|
+
"startedAt": "2026-08-14T03:44:46.167Z",
|
|
42
|
+
"logLineStart": 103,
|
|
43
|
+
"logLineEnd": 110,
|
|
44
|
+
"durationSec": 5.5
|
|
45
45
|
},
|
|
46
46
|
"build": {
|
|
47
47
|
"status": "ok",
|
|
48
|
-
"startedAt": "2026-08-
|
|
49
|
-
"logLineStart":
|
|
50
|
-
"logLineEnd":
|
|
51
|
-
"durationSec":
|
|
48
|
+
"startedAt": "2026-08-14T03:44:52.653Z",
|
|
49
|
+
"logLineStart": 111,
|
|
50
|
+
"logLineEnd": 112,
|
|
51
|
+
"durationSec": 26.6
|
|
52
52
|
},
|
|
53
53
|
"test": {
|
|
54
54
|
"status": "ok",
|
|
55
|
-
"startedAt": "2026-08-
|
|
56
|
-
"logLineStart":
|
|
57
|
-
"logLineEnd":
|
|
58
|
-
"durationSec":
|
|
55
|
+
"startedAt": "2026-08-14T03:45:20.233Z",
|
|
56
|
+
"logLineStart": 113,
|
|
57
|
+
"logLineEnd": 2856,
|
|
58
|
+
"durationSec": 32.1,
|
|
59
59
|
"testSummary": {
|
|
60
|
-
"totalSpecs":
|
|
60
|
+
"totalSpecs": 1644,
|
|
61
61
|
"failures": 0,
|
|
62
|
-
"passed":
|
|
63
|
-
"testDurationSec":
|
|
62
|
+
"passed": 1644,
|
|
63
|
+
"testDurationSec": 11.563
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"npm-publish": {
|
|
67
67
|
"status": "running",
|
|
68
|
-
"startedAt": "2026-08-
|
|
69
|
-
"logLineStart":
|
|
68
|
+
"startedAt": "2026-08-14T03:45:53.429Z",
|
|
69
|
+
"logLineStart": 2857,
|
|
70
70
|
"logLineEnd": null,
|
|
71
71
|
"durationSec": null
|
|
72
72
|
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# 🔴 `DyNTS_FieldEncryption_Util._transform` spread-elte a Mongoose-Documentet — a lista-endpointok burkot adtak vissza
|
|
2
|
+
|
|
3
|
+
**Dátum:** 2026-08-14 · **Kérő:** `BFR-MASTERPROMPTER-027` · **Súlyosság:** high (funkcionálisan törött lista-UI)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. A hiba
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
// field-encryption.util.ts — ELŐTTE
|
|
11
|
+
const result: any = Array.isArray(value) ? [ ...(value as any[]) ] : { ...(value as any) };
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Egy mongoose-**Document** adatai **nem saját mezők** — a belső `_doc`-ban ülnek. Ezért a `{ ...doc }`
|
|
15
|
+
a **belső burkot** másolja (`$__`, `$isNew`, `_doc`), és az eredmény **már nem Document**, tehát a
|
|
16
|
+
`JSON.stringify` sem hívja a `toJSON()`-ját. A kliens így ezt kapja:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
[{ "$__": {...}, "$isNew": false, "_doc": { "_id": "…", "name": "…" } }]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`_id` a legfelső szinten **nincs** → a fogyasztó minden id-alapú művelete elhasal.
|
|
23
|
+
|
|
24
|
+
### Élesben mért bizonyíték (`test.master-prompter.hu`, 2026-08-14)
|
|
25
|
+
|
|
26
|
+
| endpoint | státusz | első elem KULCSAI | `_id` |
|
|
27
|
+
|---|---|---|---|
|
|
28
|
+
| `GET /api/flow/flow/get/:userId/:flowId` (egyedi) | 200 | `_id`, `userId`, `ownerId`, `name`, … | ✅ |
|
|
29
|
+
| `GET /api/flow/flow/get-all-my/:userId` (**lista**) | 200 | **`$__`, `$isNew`, `_doc`** | ❌ **undefined** |
|
|
30
|
+
|
|
31
|
+
Kliens-oldali tünet: a lista-kártyák linkje `/editor/undefined`, a törlés
|
|
32
|
+
`DyNX_ApiService call "deleteFlow" ERROR: missing pathParam: "flowId"` dialógussal állt meg.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 2. Miért CSAK a lista tört el — a mért aszimmetria
|
|
37
|
+
|
|
38
|
+
A `DyNTS_DBService` **egyedi** olvasásai már ma is `toObject()`-tel adnak vissza sima objektumot
|
|
39
|
+
(`getDataById`, `getDataByDependencyId`, `findByIdAndUpdate`, `findOneAndUpdate`, `createData`).
|
|
40
|
+
A **lista/find metódusok NYERS Documentet** adnak tovább — kimérve **11 db**:
|
|
41
|
+
|
|
42
|
+
`getDataListByIds` · `getDataListByDependencyId` · `getDataListByDependencyIds` · `getAll` ·
|
|
43
|
+
`getDeletedDataList` · `trueDeleteAllData` · `searchData` · `findOne` · `find` · `findWithPaging` ·
|
|
44
|
+
(`findLeanBatched` — ez `.lean()`-nel már sima objektumot ad)
|
|
45
|
+
|
|
46
|
+
Titkosítás **nélkül** ez ártalmatlan: a nyers Document a `res.send`-en át a `toJSON()`-nal helyesen
|
|
47
|
+
szerializálódik. `encrypt: true` mező esetén viszont a read-post-processor (`stringifyDataId` →
|
|
48
|
+
`decryptDoc` → `_transform`) **spread-eli** a Documentet, és ezzel elrontja.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 3. A javítás
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// UTÁNA
|
|
56
|
+
const plain: any = this._toPlainIfDocument(value);
|
|
57
|
+
const result: any = Array.isArray(plain) ? [ ...(plain as any[]) ] : { ...(plain as any) };
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`_toPlainIfDocument` a `$__` **saját mező** + `toJSON` függvény kettősével azonosít egy Documentet
|
|
61
|
+
(pontosan az, ami a hibás payloadba kiszivárgott — mongoose-import nélküli, pontos diszkriminátor),
|
|
62
|
+
és `toJSON()`-t hív rá.
|
|
63
|
+
|
|
64
|
+
**Miért itt, és nem a 11 DB-metódusban:**
|
|
65
|
+
1. Ez az **egy pont** mind a 11 utat rendezi.
|
|
66
|
+
2. A 11 metódus visszatérési **típusát** megváltoztatni (Document → sima objektum) **minden modell
|
|
67
|
+
minden fogyasztóját** érintené. Az itteni javítás viszont **csak az `encrypt: true` modelleket**
|
|
68
|
+
érinti — ahol a `_transform` amúgy is **már ma is** sima (csak **rossz**) objektumot ad vissza.
|
|
69
|
+
⇒ **a változás nem tud elrontani olyat, ami ma működik.**
|
|
70
|
+
3. `toJSON()` (nem `toObject()`): a titkosítás bevezetése ELŐTT a nyers Document a `res.send`-en át a
|
|
71
|
+
`JSON.stringify` → `toJSON()` úton szerializálódott — ez adja vissza **ugyanazt a kimeneti alakot**
|
|
72
|
+
(schema-transzformok, virtuals, `__v` együtt).
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 4. 🔴 BLAST-RADIUS — a flotta érintettsége
|
|
77
|
+
|
|
78
|
+
A defekt akkor él, ha **mind a három** teljesül: (a) a modellnek van `encrypt: true` mezője ·
|
|
79
|
+
(b) a `FDP_CORE_DBCONTENT_CRYPT_KEY` be van állítva (kulcs nélkül a `decryptDoc` korán visszatér) ·
|
|
80
|
+
(c) az olvasás **lista-metóduson** megy.
|
|
81
|
+
|
|
82
|
+
| Projekt | Modell | Titkosított mezők | Állapot |
|
|
83
|
+
|---|---|---|---|
|
|
84
|
+
| **master-prompter** | `flow` | `flowSteps.prompt` | 🔴 **élesben igazoltan törött** |
|
|
85
|
+
| **master-prompter** | `flow-run` | `results`, `sourceFlow`, `flowRunValues.value` | 🔴 ugyanaz az osztály |
|
|
86
|
+
| **adventor** | `campaign` | `storyState`, `memory`, `epilogue` | ⚠️ ugyanaz az osztály — élesben nem mérve |
|
|
87
|
+
| **adventor** | `generation` | `content`, `prompt` | ⚠️ ugyanaz az osztály — élesben nem mérve |
|
|
88
|
+
| **fdp-token-service** | `token-purchase` | `requestIp`, `euVatNumber` (PII) | ⚠️ ugyanaz az osztály — élesben nem mérve |
|
|
89
|
+
| **fdp-token-service** | `manual-invoice` | `realID`, `title`, `fullName`, `companyName`, … | ⚠️ ugyanaz az osztály — élesben nem mérve |
|
|
90
|
+
|
|
91
|
+
⇒ **6 modell, 3 projekt.** A master-prompteren kívül egyik sincs élesben visszamérve — ezeket a
|
|
92
|
+
projektek `nts-dynamo`-bumpja után kell ellenőrizni (a lista-endpointjaik `_id`-t adnak-e).
|
|
93
|
+
A `manual-invoice` és a `token-purchase` külön figyelmet érdemel: számlázási/PII adat.
|
|
94
|
+
|
|
95
|
+
> **Miért nem tűnt fel eddig:** a szerver **200**-at ad, a válasz JSON-ként **érvényes** — csak épp a
|
|
96
|
+
> burkot tartalmazza. A UI „üresnek"/működésképtelennek látszik, de sehol nincs hibajelzés.
|
|
97
|
+
> Az MP-ben azért derült ki, mert az `e2e-feature` step (a 30m→60m timeout-emelés után) **most futott
|
|
98
|
+
> le először végig**.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 5. Kontroll-mérés — VALÓDI mongoose-Documenttel
|
|
103
|
+
|
|
104
|
+
A regresszió-őr **szándékosan nem mockkal** dolgozik: a hiba pont abból eredt, hogy a Document adatai
|
|
105
|
+
nem saját mezők — egy kézzel gyártott mock ezt a jellemzőt nem hordozza, tehát a mock-alapú teszt
|
|
106
|
+
**akkor is zöld maradna**, ha a hiba visszakerülne. DB-kapcsolat nem kell: a `new Model({...})`
|
|
107
|
+
offline is valódi Documentet ad.
|
|
108
|
+
|
|
109
|
+
| Eset | A fix nélkül |
|
|
110
|
+
|---|---|
|
|
111
|
+
| a visszafejtett eredmény sima objektum (nincs `$__`/`$isNew`/`_doc`) | ❌ `Expected [ '$__', '_doc' ] not to contain '$__'` |
|
|
112
|
+
| az `_id` megmarad | ❌ `az _id elveszett a transzform során: Expected undefined to be defined` |
|
|
113
|
+
| a JSON-szerializált alak a valódi adatot adja | ❌ `… not to contain '_doc'` |
|
|
114
|
+
| Document-**tömb** (lista-alak) minden eleme sima objektum, `_id`-vel | ❌ `… not to contain '_doc'` |
|
|
115
|
+
| **összesen** | ❌ **4 FAILED** → javítva: ✅ **1644 specs, 0 failures** |
|
|
116
|
+
|
|
117
|
+
A mezők helyes visszafejtését is asszertáljuk Documenten (top-level + beágyazott tömb-elem).
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 6. Adopció
|
|
122
|
+
|
|
123
|
+
`@futdevpro/nts-dynamo` bump minden érintett szerver-projektben (master-prompter, adventor,
|
|
124
|
+
fdp-token-service), majd a lista-endpointok visszamérése (`_id` jelen van-e a válaszban).
|
|
@@ -42,10 +42,44 @@ export declare class DyNTS_FieldEncryption_Util {
|
|
|
42
42
|
* (a read ne törjön el) — az envelope-értékek visszafejtetlenül maradnak (a hívó látja + a hiba loggolt).
|
|
43
43
|
*/
|
|
44
44
|
static decryptDoc<T>(data: T, properties?: DyFM_DataProperties<any>): T;
|
|
45
|
+
/**
|
|
46
|
+
* Egy mongoose-**Document** sima objektummá alakítása (más érték változatlanul jön vissza).
|
|
47
|
+
*
|
|
48
|
+
* @description 🔴 **MIÉRT KELL** (BFR-MASTERPROMPTER-027, 2026-08-14): a `_transform` sekély spreaddel
|
|
49
|
+
* klónoz. Egy mongoose-Documentnek viszont az adatok NEM saját mezői — azok a belső `_doc`-ban
|
|
50
|
+
* ülnek —, ezért a `{ ...doc }` a **belső burkot** másolja (`$__`, `$isNew`, `_doc`), és az eredmény
|
|
51
|
+
* **már nem Document**, tehát a `JSON.stringify` sem hívja a `toJSON()`-ját. A kliens így
|
|
52
|
+
* `{"$__":…,"$isNew":…,"_doc":{…}}`-t kap `_id` nélkül.
|
|
53
|
+
*
|
|
54
|
+
* **Élesben mérve** (`test.master-prompter.hu`, 2026-08-14): a `flow` lista-endpoint válaszának
|
|
55
|
+
* első elemén a kulcsok `["$__","$isNew","_doc"]`, `_id: undefined` → a kliens-oldali linkek
|
|
56
|
+
* `/editor/undefined`-ra mutattak, a törlés `missing pathParam: "flowId"`-vel állt meg. Az EGYEDI
|
|
57
|
+
* get ugyanakkor ép volt — mert a `DyNTS_DBService` az egyedi olvasásoknál **már `toObject()`-et**
|
|
58
|
+
* hív (`db.service.ts` getDataById / getDataByDependencyId / findByIdAndUpdate / findOneAndUpdate),
|
|
59
|
+
* a **lista-metódusok (11 db) viszont NYERS Documentet adnak tovább**. Titkosítás nélküli
|
|
60
|
+
* modelleknél ez ártalmatlan (a `res.send` a `toJSON()`-on át szerializál), `encrypt:true` mező
|
|
61
|
+
* esetén viszont a spread elrontja.
|
|
62
|
+
*
|
|
63
|
+
* **Miért ITT javítjuk, és nem a 11 DB-metódusban:** (1) ez az EGY pont mind a 11 utat rendezi;
|
|
64
|
+
* (2) a 11 metódus visszatérési TÍPUSÁT megváltoztatni (Document → sima objektum) minden modell
|
|
65
|
+
* minden fogyasztóját érintené — az itteni javítás viszont **csak az `encrypt:true` modelleket**
|
|
66
|
+
* érinti, ahol a `_transform` amúgy is **már ma is** sima (csak ROSSZ) objektumot ad vissza.
|
|
67
|
+
* Vagyis a változás nem tud elrontani olyat, ami ma működik.
|
|
68
|
+
*
|
|
69
|
+
* `toJSON()`-t használunk (nem `toObject()`-et): a titkosítás bevezetése ELŐTT a nyers Document a
|
|
70
|
+
* `res.send`-en át a `JSON.stringify` → `toJSON()` úton szerializálódott, tehát ez adja vissza
|
|
71
|
+
* **bitre ugyanazt a kimeneti alakot** (schema-transzformok, virtuals, `__v`-kezelés együtt).
|
|
72
|
+
*
|
|
73
|
+
* @param value tetszőleges érték
|
|
74
|
+
* @returns a Document sima alakja, vagy az eredeti érték
|
|
75
|
+
*/
|
|
76
|
+
private static _toPlainIfDocument;
|
|
45
77
|
/**
|
|
46
78
|
* Rekurzív per-field walk: az `encrypt:true` mezőket titkosítja/visszafejti, a `subObjectParams`-os
|
|
47
79
|
* mezőkbe (objektum VAGY objektum-tömb) leszáll. Immutábilis: minden érintett konténert sekélyen
|
|
48
80
|
* klónoz (az eredetit nem mutálja); a nem-érintett ágakat referencia szerint megosztja (read-only).
|
|
81
|
+
*
|
|
82
|
+
* ⚠️ A klónozás ELŐTT a mongoose-Documentek sima objektummá alakulnak — lásd {@link _toPlainIfDocument}.
|
|
49
83
|
*/
|
|
50
84
|
private static _transform;
|
|
51
85
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-encryption.util.d.ts","sourceRoot":"","sources":["../../src/_collections/field-encryption.util.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EAEpB,MAAM,uBAAuB,CAAC;AAG/B;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,0BAA0B;IACrC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAkC;IACjE,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAA0C;IAEjF,+EAA+E;IAC/E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAA2C;IAE/E,mEAAmE;IACnE,MAAM,CAAC,MAAM,IAAI,MAAM;IAIvB,4CAA4C;IAC5C,MAAM,CAAC,aAAa,IAAI,MAAM;IAK9B;;;OAGG;IACH,MAAM,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAC,GAAG,CAAC,GAAG,OAAO;IA8BzE;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,CAAC,EAAE,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC;IAmBvE;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,CAAC,EAAE,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC;IAavE
|
|
1
|
+
{"version":3,"file":"field-encryption.util.d.ts","sourceRoot":"","sources":["../../src/_collections/field-encryption.util.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EAEpB,MAAM,uBAAuB,CAAC;AAG/B;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,0BAA0B;IACrC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAkC;IACjE,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAA0C;IAEjF,+EAA+E;IAC/E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAA2C;IAE/E,mEAAmE;IACnE,MAAM,CAAC,MAAM,IAAI,MAAM;IAIvB,4CAA4C;IAC5C,MAAM,CAAC,aAAa,IAAI,MAAM;IAK9B;;;OAGG;IACH,MAAM,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAC,GAAG,CAAC,GAAG,OAAO;IA8BzE;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,CAAC,EAAE,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC;IAmBvE;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,CAAC,EAAE,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC;IAavE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAejC;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;CAmD1B"}
|
|
@@ -99,16 +99,61 @@ class DyNTS_FieldEncryption_Util {
|
|
|
99
99
|
}
|
|
100
100
|
return this._transform(data, properties, 'decrypt', key, this.getKeyVersion());
|
|
101
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Egy mongoose-**Document** sima objektummá alakítása (más érték változatlanul jön vissza).
|
|
104
|
+
*
|
|
105
|
+
* @description 🔴 **MIÉRT KELL** (BFR-MASTERPROMPTER-027, 2026-08-14): a `_transform` sekély spreaddel
|
|
106
|
+
* klónoz. Egy mongoose-Documentnek viszont az adatok NEM saját mezői — azok a belső `_doc`-ban
|
|
107
|
+
* ülnek —, ezért a `{ ...doc }` a **belső burkot** másolja (`$__`, `$isNew`, `_doc`), és az eredmény
|
|
108
|
+
* **már nem Document**, tehát a `JSON.stringify` sem hívja a `toJSON()`-ját. A kliens így
|
|
109
|
+
* `{"$__":…,"$isNew":…,"_doc":{…}}`-t kap `_id` nélkül.
|
|
110
|
+
*
|
|
111
|
+
* **Élesben mérve** (`test.master-prompter.hu`, 2026-08-14): a `flow` lista-endpoint válaszának
|
|
112
|
+
* első elemén a kulcsok `["$__","$isNew","_doc"]`, `_id: undefined` → a kliens-oldali linkek
|
|
113
|
+
* `/editor/undefined`-ra mutattak, a törlés `missing pathParam: "flowId"`-vel állt meg. Az EGYEDI
|
|
114
|
+
* get ugyanakkor ép volt — mert a `DyNTS_DBService` az egyedi olvasásoknál **már `toObject()`-et**
|
|
115
|
+
* hív (`db.service.ts` getDataById / getDataByDependencyId / findByIdAndUpdate / findOneAndUpdate),
|
|
116
|
+
* a **lista-metódusok (11 db) viszont NYERS Documentet adnak tovább**. Titkosítás nélküli
|
|
117
|
+
* modelleknél ez ártalmatlan (a `res.send` a `toJSON()`-on át szerializál), `encrypt:true` mező
|
|
118
|
+
* esetén viszont a spread elrontja.
|
|
119
|
+
*
|
|
120
|
+
* **Miért ITT javítjuk, és nem a 11 DB-metódusban:** (1) ez az EGY pont mind a 11 utat rendezi;
|
|
121
|
+
* (2) a 11 metódus visszatérési TÍPUSÁT megváltoztatni (Document → sima objektum) minden modell
|
|
122
|
+
* minden fogyasztóját érintené — az itteni javítás viszont **csak az `encrypt:true` modelleket**
|
|
123
|
+
* érinti, ahol a `_transform` amúgy is **már ma is** sima (csak ROSSZ) objektumot ad vissza.
|
|
124
|
+
* Vagyis a változás nem tud elrontani olyat, ami ma működik.
|
|
125
|
+
*
|
|
126
|
+
* `toJSON()`-t használunk (nem `toObject()`-et): a titkosítás bevezetése ELŐTT a nyers Document a
|
|
127
|
+
* `res.send`-en át a `JSON.stringify` → `toJSON()` úton szerializálódott, tehát ez adja vissza
|
|
128
|
+
* **bitre ugyanazt a kimeneti alakot** (schema-transzformok, virtuals, `__v`-kezelés együtt).
|
|
129
|
+
*
|
|
130
|
+
* @param value tetszőleges érték
|
|
131
|
+
* @returns a Document sima alakja, vagy az eredeti érték
|
|
132
|
+
*/
|
|
133
|
+
static _toPlainIfDocument(value) {
|
|
134
|
+
// A `$__` a mongoose-Document SAJÁT mezője — pontosan ez szivárgott ki a hibás payloadba,
|
|
135
|
+
// ezért ez a legpontosabb (és mongoose-import nélküli) diszkriminátor.
|
|
136
|
+
if (value != null
|
|
137
|
+
&& typeof value === 'object'
|
|
138
|
+
&& typeof value.toJSON === 'function'
|
|
139
|
+
&& Object.prototype.hasOwnProperty.call(value, '$__')) {
|
|
140
|
+
return value.toJSON();
|
|
141
|
+
}
|
|
142
|
+
return value;
|
|
143
|
+
}
|
|
102
144
|
/**
|
|
103
145
|
* Rekurzív per-field walk: az `encrypt:true` mezőket titkosítja/visszafejti, a `subObjectParams`-os
|
|
104
146
|
* mezőkbe (objektum VAGY objektum-tömb) leszáll. Immutábilis: minden érintett konténert sekélyen
|
|
105
147
|
* klónoz (az eredetit nem mutálja); a nem-érintett ágakat referencia szerint megosztja (read-only).
|
|
148
|
+
*
|
|
149
|
+
* ⚠️ A klónozás ELŐTT a mongoose-Documentek sima objektummá alakulnak — lásd {@link _toPlainIfDocument}.
|
|
106
150
|
*/
|
|
107
151
|
static _transform(value, properties, mode, key, keyVersion) {
|
|
108
152
|
if (value == null || typeof value !== 'object') {
|
|
109
153
|
return value;
|
|
110
154
|
}
|
|
111
|
-
const
|
|
155
|
+
const plain = this._toPlainIfDocument(value); // ccap-review-disable-line no-any-type
|
|
156
|
+
const result = Array.isArray(plain) ? [...plain] : { ...plain }; // ccap-review-disable-line no-any-type
|
|
112
157
|
for (const propKey in properties) {
|
|
113
158
|
const prop = properties[propKey]; // ccap-review-disable-line no-any-type
|
|
114
159
|
if (!prop) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-encryption.util.js","sourceRoot":"","sources":["../../src/_collections/field-encryption.util.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"field-encryption.util.js","sourceRoot":"","sources":["../../src/_collections/field-encryption.util.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@futdevpro/nts-dynamo",
|
|
3
|
-
"version": "01.15.
|
|
3
|
+
"version": "01.15.136",
|
|
4
4
|
"description": "Dynamic NodeTS (NodeJS-Typescript), MongoDB Backend System Framework by Future Development Program Ltd.",
|
|
5
5
|
"DyBu_settings": {
|
|
6
6
|
"packageType": "server-package",
|
|
@@ -378,7 +378,7 @@
|
|
|
378
378
|
"@anthropic-ai/sdk": "~0.39.0",
|
|
379
379
|
"@modelcontextprotocol/sdk": "~1.29.0",
|
|
380
380
|
"better-sqlite3": "~12.4.1",
|
|
381
|
-
"@futdevpro/fsm-dynamo": "1.16.
|
|
381
|
+
"@futdevpro/fsm-dynamo": "1.16.38",
|
|
382
382
|
"@types/express": "4.17.21",
|
|
383
383
|
"@types/geoip-lite": "~1.4.1",
|
|
384
384
|
"@types/node": "~24.1.0",
|
|
@@ -412,7 +412,7 @@
|
|
|
412
412
|
}
|
|
413
413
|
},
|
|
414
414
|
"devDependencies": {
|
|
415
|
-
"@futdevpro/dynamo-eslint": "1.15.
|
|
415
|
+
"@futdevpro/dynamo-eslint": "1.15.25",
|
|
416
416
|
"@discordjs/opus": "^0.10.0",
|
|
417
417
|
"@discordjs/voice": "^0.18.0",
|
|
418
418
|
"@types/jasmine": "~4.3.5",
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Schema, model } from 'mongoose';
|
|
2
|
+
|
|
1
3
|
import { DyFM_DataProperties } from '@futdevpro/fsm-dynamo';
|
|
2
4
|
import { DyFM_Crypto } from '@futdevpro/fsm-dynamo/crypto';
|
|
3
5
|
|
|
@@ -106,4 +108,88 @@ describe('| DyNTS_FieldEncryption_Util', () => {
|
|
|
106
108
|
const dec: any = DyNTS_FieldEncryption_Util.decryptDoc(enc, encProps);
|
|
107
109
|
expect(dec.secret).toBe(enc.secret); // envelope marad, nincs throw
|
|
108
110
|
});
|
|
111
|
+
|
|
112
|
+
// ─── 🔴 MONGOOSE-DOCUMENT (BFR-MASTERPROMPTER-027) ─────────────────────────────────────────
|
|
113
|
+
//
|
|
114
|
+
// A regresszio-or SZANDEKOSAN VALODI mongoose-Documenttel dolgozik (nem mockkal): a hiba pont abbol
|
|
115
|
+
// eredt, hogy egy Document adatai NEM sajat mezok (a `_doc`-ban ulnek), ezert a `{ ...doc }` a belso
|
|
116
|
+
// burkot masolja. Egy kezzel gyartott mock ezt a jellemzot nem hordozza — vagyis a mock-alapu teszt
|
|
117
|
+
// AKKOR IS zold maradna, ha a hiba visszakerulne.
|
|
118
|
+
//
|
|
119
|
+
// DB-kapcsolat NEM kell: a `new Model({...})` offline is valodi Documentet ad.
|
|
120
|
+
describe('| mongoose-Document bemenet (BFR-MASTERPROMPTER-027)', () => {
|
|
121
|
+
const stepSchema: Schema = new Schema({ prompt: String, label: String }, { _id: false });
|
|
122
|
+
const flowSchema: Schema = new Schema({ name: String, secret: String, steps: [ stepSchema ] });
|
|
123
|
+
const FlowModel = model('EncUtilSpecFlow', flowSchema);
|
|
124
|
+
|
|
125
|
+
function newDocument(): any { // ccap-review-disable-line no-any-type
|
|
126
|
+
return new FlowModel({
|
|
127
|
+
name: 'my flow',
|
|
128
|
+
secret: DyFM_Crypto.encryptDbValue('titok', KEY, 1),
|
|
129
|
+
steps: [ { prompt: DyFM_Crypto.encryptDbValue('lepes-prompt', KEY, 1), label: 'A' } ],
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
it('| a visszafejtett eredmeny SIMA objektum — NINCS benne $__ / $isNew / _doc', () => {
|
|
134
|
+
const doc: any = newDocument(); // ccap-review-disable-line no-any-type
|
|
135
|
+
|
|
136
|
+
const result: any = DyNTS_FieldEncryption_Util.decryptDoc(doc, encProps); // ccap-review-disable-line no-any-type
|
|
137
|
+
|
|
138
|
+
expect(Object.keys(result)).not.toContain('$__');
|
|
139
|
+
expect(Object.keys(result)).not.toContain('$isNew');
|
|
140
|
+
expect(Object.keys(result)).not.toContain('_doc');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
/** ⚠️ EZ a fazis lenyegi kontroll-assertje: a kliens ezt az `_id`-t nem kapta meg. */
|
|
144
|
+
it('| az `_id` MEGMARAD (a kliens enelkul nem tud torolni/megnyitni)', () => {
|
|
145
|
+
const doc: any = newDocument(); // ccap-review-disable-line no-any-type
|
|
146
|
+
|
|
147
|
+
const result: any = DyNTS_FieldEncryption_Util.decryptDoc(doc, encProps); // ccap-review-disable-line no-any-type
|
|
148
|
+
|
|
149
|
+
expect(result._id).withContext('az _id elveszett a transzform soran').toBeDefined();
|
|
150
|
+
expect(`${result._id}`).toBe(`${doc._id}`);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('| az adat-mezok es a visszafejtes helyesek Documenten is', () => {
|
|
154
|
+
const doc: any = newDocument(); // ccap-review-disable-line no-any-type
|
|
155
|
+
|
|
156
|
+
const result: any = DyNTS_FieldEncryption_Util.decryptDoc(doc, encProps); // ccap-review-disable-line no-any-type
|
|
157
|
+
|
|
158
|
+
expect(result.name).toBe('my flow');
|
|
159
|
+
expect(result.secret).withContext('a top-level enc-mezo nem fejtodott vissza').toBe('titok');
|
|
160
|
+
expect(result.steps[0].prompt).withContext('a beagyazott enc-mezo nem fejtodott vissza')
|
|
161
|
+
.toBe('lepes-prompt');
|
|
162
|
+
expect(result.steps[0].label).toBe('A');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* A JSON-korut is merjuk: a hiba VEGSO tunete az volt, hogy a `res.send` a burkot szerializalta.
|
|
167
|
+
* Ez az assert azt a kimenetet nezi, amit a kliens TENYLEGESEN megkap.
|
|
168
|
+
*/
|
|
169
|
+
it('| a JSON-szerializalt alak a valodi adatot adja (a kliens ezt kapja)', () => {
|
|
170
|
+
const doc: any = newDocument(); // ccap-review-disable-line no-any-type
|
|
171
|
+
|
|
172
|
+
const serialized: Record<string, unknown> =
|
|
173
|
+
JSON.parse(JSON.stringify(DyNTS_FieldEncryption_Util.decryptDoc(doc, encProps)));
|
|
174
|
+
|
|
175
|
+
expect(serialized['_id']).toBeDefined();
|
|
176
|
+
expect(serialized['name']).toBe('my flow');
|
|
177
|
+
expect(Object.keys(serialized)).not.toContain('_doc');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('| Document-TOMB (lista-endpoint alak) minden eleme sima objektum, _id-vel', () => {
|
|
181
|
+
const list: any[] = [ newDocument(), newDocument() ]; // ccap-review-disable-line no-any-type
|
|
182
|
+
|
|
183
|
+
const results: any[] = list.map( // ccap-review-disable-line no-any-type
|
|
184
|
+
(d: any): any => DyNTS_FieldEncryption_Util.decryptDoc(d, encProps), // ccap-review-disable-line no-any-type
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
results.forEach((r: any): void => { // ccap-review-disable-line no-any-type
|
|
188
|
+
expect(r._id).toBeDefined();
|
|
189
|
+
expect(Object.keys(r)).not.toContain('_doc');
|
|
190
|
+
expect(r.secret).toBe('titok');
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
109
195
|
});
|
|
@@ -115,10 +115,58 @@ export class DyNTS_FieldEncryption_Util {
|
|
|
115
115
|
return this._transform(data, properties as DyFM_DataProperties<any>, 'decrypt', key, this.getKeyVersion());
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Egy mongoose-**Document** sima objektummá alakítása (más érték változatlanul jön vissza).
|
|
120
|
+
*
|
|
121
|
+
* @description 🔴 **MIÉRT KELL** (BFR-MASTERPROMPTER-027, 2026-08-14): a `_transform` sekély spreaddel
|
|
122
|
+
* klónoz. Egy mongoose-Documentnek viszont az adatok NEM saját mezői — azok a belső `_doc`-ban
|
|
123
|
+
* ülnek —, ezért a `{ ...doc }` a **belső burkot** másolja (`$__`, `$isNew`, `_doc`), és az eredmény
|
|
124
|
+
* **már nem Document**, tehát a `JSON.stringify` sem hívja a `toJSON()`-ját. A kliens így
|
|
125
|
+
* `{"$__":…,"$isNew":…,"_doc":{…}}`-t kap `_id` nélkül.
|
|
126
|
+
*
|
|
127
|
+
* **Élesben mérve** (`test.master-prompter.hu`, 2026-08-14): a `flow` lista-endpoint válaszának
|
|
128
|
+
* első elemén a kulcsok `["$__","$isNew","_doc"]`, `_id: undefined` → a kliens-oldali linkek
|
|
129
|
+
* `/editor/undefined`-ra mutattak, a törlés `missing pathParam: "flowId"`-vel állt meg. Az EGYEDI
|
|
130
|
+
* get ugyanakkor ép volt — mert a `DyNTS_DBService` az egyedi olvasásoknál **már `toObject()`-et**
|
|
131
|
+
* hív (`db.service.ts` getDataById / getDataByDependencyId / findByIdAndUpdate / findOneAndUpdate),
|
|
132
|
+
* a **lista-metódusok (11 db) viszont NYERS Documentet adnak tovább**. Titkosítás nélküli
|
|
133
|
+
* modelleknél ez ártalmatlan (a `res.send` a `toJSON()`-on át szerializál), `encrypt:true` mező
|
|
134
|
+
* esetén viszont a spread elrontja.
|
|
135
|
+
*
|
|
136
|
+
* **Miért ITT javítjuk, és nem a 11 DB-metódusban:** (1) ez az EGY pont mind a 11 utat rendezi;
|
|
137
|
+
* (2) a 11 metódus visszatérési TÍPUSÁT megváltoztatni (Document → sima objektum) minden modell
|
|
138
|
+
* minden fogyasztóját érintené — az itteni javítás viszont **csak az `encrypt:true` modelleket**
|
|
139
|
+
* érinti, ahol a `_transform` amúgy is **már ma is** sima (csak ROSSZ) objektumot ad vissza.
|
|
140
|
+
* Vagyis a változás nem tud elrontani olyat, ami ma működik.
|
|
141
|
+
*
|
|
142
|
+
* `toJSON()`-t használunk (nem `toObject()`-et): a titkosítás bevezetése ELŐTT a nyers Document a
|
|
143
|
+
* `res.send`-en át a `JSON.stringify` → `toJSON()` úton szerializálódott, tehát ez adja vissza
|
|
144
|
+
* **bitre ugyanazt a kimeneti alakot** (schema-transzformok, virtuals, `__v`-kezelés együtt).
|
|
145
|
+
*
|
|
146
|
+
* @param value tetszőleges érték
|
|
147
|
+
* @returns a Document sima alakja, vagy az eredeti érték
|
|
148
|
+
*/
|
|
149
|
+
private static _toPlainIfDocument(value: any): any { // ccap-review-disable-line no-any-type
|
|
150
|
+
// A `$__` a mongoose-Document SAJÁT mezője — pontosan ez szivárgott ki a hibás payloadba,
|
|
151
|
+
// ezért ez a legpontosabb (és mongoose-import nélküli) diszkriminátor.
|
|
152
|
+
if (
|
|
153
|
+
value != null
|
|
154
|
+
&& typeof value === 'object'
|
|
155
|
+
&& typeof value.toJSON === 'function'
|
|
156
|
+
&& Object.prototype.hasOwnProperty.call(value, '$__')
|
|
157
|
+
) {
|
|
158
|
+
return value.toJSON();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return value;
|
|
162
|
+
}
|
|
163
|
+
|
|
118
164
|
/**
|
|
119
165
|
* Rekurzív per-field walk: az `encrypt:true` mezőket titkosítja/visszafejti, a `subObjectParams`-os
|
|
120
166
|
* mezőkbe (objektum VAGY objektum-tömb) leszáll. Immutábilis: minden érintett konténert sekélyen
|
|
121
167
|
* klónoz (az eredetit nem mutálja); a nem-érintett ágakat referencia szerint megosztja (read-only).
|
|
168
|
+
*
|
|
169
|
+
* ⚠️ A klónozás ELŐTT a mongoose-Documentek sima objektummá alakulnak — lásd {@link _toPlainIfDocument}.
|
|
122
170
|
*/
|
|
123
171
|
private static _transform<T>(
|
|
124
172
|
value: T,
|
|
@@ -131,7 +179,8 @@ export class DyNTS_FieldEncryption_Util {
|
|
|
131
179
|
return value;
|
|
132
180
|
}
|
|
133
181
|
|
|
134
|
-
const
|
|
182
|
+
const plain: any = this._toPlainIfDocument(value); // ccap-review-disable-line no-any-type
|
|
183
|
+
const result: any = Array.isArray(plain) ? [ ...(plain as any[]) ] : { ...(plain as any) }; // ccap-review-disable-line no-any-type
|
|
135
184
|
|
|
136
185
|
for (const propKey in properties) {
|
|
137
186
|
const prop: any = properties[propKey]; // ccap-review-disable-line no-any-type
|