test-entity-library-asm 3.9.6 → 3.9.8
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.
|
@@ -4,53 +4,105 @@ exports.JsonEncryptionTransformer = void 0;
|
|
|
4
4
|
const crypto = require("crypto");
|
|
5
5
|
const ENCRYPTION_KEY = process.env.SECRET_ENCRYPTION_KEY || "clave-32-bytes-segura";
|
|
6
6
|
const ALGORITHM = "aes-256-cbc";
|
|
7
|
-
//
|
|
8
|
-
const
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
// KeyObject de 32 bytes
|
|
8
|
+
const KEY = crypto.createSecretKey(crypto.createHash("sha256").update(String(ENCRYPTION_KEY)).digest());
|
|
9
|
+
function looksLikeCBC(encoded) {
|
|
10
|
+
// formato: iv:cipherHex
|
|
11
|
+
return /^[0-9a-fA-F]+:[0-9a-fA-F]+$/.test(encoded);
|
|
12
|
+
}
|
|
13
|
+
function decryptValue(value) {
|
|
14
|
+
if (typeof value !== "string" || !looksLikeCBC(value))
|
|
15
|
+
return value;
|
|
16
|
+
const [ivHex, contentHex] = value.split(":");
|
|
13
17
|
const iv = Buffer.from(ivHex, "hex");
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
const decipher = crypto.createDecipheriv(ALGORITHM, KEY, iv);
|
|
19
|
+
const dec = Buffer.concat([
|
|
20
|
+
decipher.update(Buffer.from(contentHex, "hex")),
|
|
21
|
+
decipher.final(),
|
|
22
|
+
]);
|
|
23
|
+
return dec.toString("utf8");
|
|
19
24
|
}
|
|
20
25
|
function encryptValue(plain) {
|
|
26
|
+
if (plain === null || plain === undefined)
|
|
27
|
+
return null;
|
|
28
|
+
const text = String(plain);
|
|
21
29
|
const iv = crypto.randomBytes(16);
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
encrypted += cipher.final("hex");
|
|
26
|
-
return iv.toString("hex") + ":" + encrypted;
|
|
30
|
+
const cipher = crypto.createCipheriv(ALGORITHM, KEY, iv);
|
|
31
|
+
const enc = Buffer.concat([cipher.update(text, "utf8"), cipher.final()]);
|
|
32
|
+
return iv.toString("hex") + ":" + enc.toString("hex");
|
|
27
33
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
/** Recorre profundo y encripta solo claves `encrypted_*` */
|
|
35
|
+
function deepEncrypt(input) {
|
|
36
|
+
if (input === null || input === undefined)
|
|
37
|
+
return input;
|
|
38
|
+
if (Array.isArray(input)) {
|
|
39
|
+
const arr = [];
|
|
40
|
+
for (let i = 0; i < input.length; i++) {
|
|
41
|
+
arr[i] = deepEncrypt(input[i]);
|
|
42
|
+
}
|
|
43
|
+
return arr;
|
|
44
|
+
}
|
|
45
|
+
if (typeof input === "object") {
|
|
46
|
+
const out = {};
|
|
47
|
+
for (const k in input) {
|
|
48
|
+
const v = input[k];
|
|
49
|
+
if (k.length >= 10 && k.substr(0, 10) === "encrypted_") {
|
|
50
|
+
out[k] = encryptValue(v);
|
|
36
51
|
}
|
|
37
52
|
else {
|
|
38
|
-
|
|
53
|
+
out[k] = deepEncrypt(v);
|
|
39
54
|
}
|
|
40
55
|
}
|
|
41
|
-
return
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
// primitivos
|
|
59
|
+
return input;
|
|
60
|
+
}
|
|
61
|
+
/** Recorre profundo y desencripta solo claves `encrypted_*` */
|
|
62
|
+
function deepDecrypt(input) {
|
|
63
|
+
if (input === null || input === undefined)
|
|
64
|
+
return input;
|
|
65
|
+
if (Array.isArray(input)) {
|
|
66
|
+
const arr = [];
|
|
67
|
+
for (let i = 0; i < input.length; i++) {
|
|
68
|
+
arr[i] = deepDecrypt(input[i]);
|
|
69
|
+
}
|
|
70
|
+
return arr;
|
|
71
|
+
}
|
|
72
|
+
if (typeof input === "object") {
|
|
73
|
+
const out = {};
|
|
74
|
+
for (const k in input) {
|
|
75
|
+
const v = input[k];
|
|
76
|
+
if (k.length >= 10 && k.substr(0, 10) === "encrypted_") {
|
|
77
|
+
try {
|
|
78
|
+
out[k] = decryptValue(v);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
out[k] = v; // si está corrupto, lo dejamos como está
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
out[k] = deepDecrypt(v);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
// primitivos
|
|
91
|
+
return input;
|
|
92
|
+
}
|
|
93
|
+
exports.JsonEncryptionTransformer = {
|
|
94
|
+
to(value) {
|
|
95
|
+
if (typeof value !== "object" || value === null)
|
|
96
|
+
return JSON.stringify({});
|
|
97
|
+
return JSON.stringify(deepEncrypt(value));
|
|
42
98
|
},
|
|
43
99
|
from(dbValue) {
|
|
44
100
|
try {
|
|
45
101
|
const parsed = JSON.parse(dbValue);
|
|
46
|
-
|
|
47
|
-
if (key.startsWith("encrypted_")) {
|
|
48
|
-
parsed[key] = decryptValue(parsed[key]);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return parsed;
|
|
102
|
+
return deepDecrypt(parsed);
|
|
52
103
|
}
|
|
53
104
|
catch {
|
|
105
|
+
// si no es JSON válido, no tumbar todo
|
|
54
106
|
return {};
|
|
55
107
|
}
|
|
56
108
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "test-entity-library-asm",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.8",
|
|
4
4
|
"description": "Entidades de ejemplo para una base de datos",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/express": "^4.17.21",
|
|
23
|
-
"@types/node": "^20.
|
|
23
|
+
"@types/node": "^20.19.10",
|
|
24
24
|
"rimraf": "^6.0.1",
|
|
25
25
|
"typescript": "^5.4.5"
|
|
26
26
|
},
|
|
@@ -5,70 +5,111 @@ const ENCRYPTION_KEY =
|
|
|
5
5
|
process.env.SECRET_ENCRYPTION_KEY || "clave-32-bytes-segura";
|
|
6
6
|
const ALGORITHM = "aes-256-cbc";
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
const
|
|
10
|
-
|
|
8
|
+
// KeyObject de 32 bytes
|
|
9
|
+
const KEY = crypto.createSecretKey(
|
|
10
|
+
crypto.createHash("sha256").update(String(ENCRYPTION_KEY)).digest()
|
|
11
11
|
);
|
|
12
12
|
|
|
13
|
-
function
|
|
14
|
-
|
|
13
|
+
function looksLikeCBC(encoded: string) {
|
|
14
|
+
// formato: iv:cipherHex
|
|
15
|
+
return /^[0-9a-fA-F]+:[0-9a-fA-F]+$/.test(encoded);
|
|
16
|
+
}
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
function decryptValue(value: any): any {
|
|
19
|
+
if (typeof value !== "string" || !looksLikeCBC(value)) return value;
|
|
20
|
+
const [ivHex, contentHex] = value.split(":");
|
|
17
21
|
const iv = Buffer.from(ivHex, "hex");
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
);
|
|
25
|
-
let decrypted = decipher.update(content, "hex", "utf8");
|
|
26
|
-
decrypted += decipher.final("utf8");
|
|
27
|
-
return decrypted;
|
|
22
|
+
const decipher = crypto.createDecipheriv(ALGORITHM, KEY, iv);
|
|
23
|
+
const dec = Buffer.concat([
|
|
24
|
+
decipher.update(Buffer.from(contentHex, "hex")),
|
|
25
|
+
decipher.final(),
|
|
26
|
+
]);
|
|
27
|
+
return dec.toString("utf8");
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
function encryptValue(plain:
|
|
30
|
+
function encryptValue(plain: any): string | null {
|
|
31
|
+
if (plain === null || plain === undefined) return null;
|
|
32
|
+
const text = String(plain);
|
|
31
33
|
const iv = crypto.randomBytes(16);
|
|
34
|
+
const cipher = crypto.createCipheriv(ALGORITHM, KEY, iv);
|
|
35
|
+
const enc = Buffer.concat([cipher.update(text, "utf8"), cipher.final()]);
|
|
36
|
+
return iv.toString("hex") + ":" + enc.toString("hex");
|
|
37
|
+
}
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
keyObject,
|
|
37
|
-
new Uint8Array(iv)
|
|
38
|
-
);
|
|
39
|
-
let encrypted = cipher.update(plain, "utf8", "hex");
|
|
40
|
-
encrypted += cipher.final("hex");
|
|
39
|
+
/** Recorre profundo y encripta solo claves `encrypted_*` */
|
|
40
|
+
function deepEncrypt(input: any): any {
|
|
41
|
+
if (input === null || input === undefined) return input;
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
if (Array.isArray(input)) {
|
|
44
|
+
const arr: any[] = [];
|
|
45
|
+
for (let i = 0; i < input.length; i++) {
|
|
46
|
+
arr[i] = deepEncrypt(input[i]);
|
|
47
|
+
}
|
|
48
|
+
return arr;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (typeof input === "object") {
|
|
52
|
+
const out: { [k: string]: any } = {};
|
|
53
|
+
for (const k in input) {
|
|
54
|
+
const v = input[k];
|
|
55
|
+
if (k.length >= 10 && k.substr(0, 10) === "encrypted_") {
|
|
56
|
+
out[k] = encryptValue(v);
|
|
57
|
+
} else {
|
|
58
|
+
out[k] = deepEncrypt(v);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// primitivos
|
|
65
|
+
return input;
|
|
43
66
|
}
|
|
44
67
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
68
|
+
/** Recorre profundo y desencripta solo claves `encrypted_*` */
|
|
69
|
+
function deepDecrypt(input: any): any {
|
|
70
|
+
if (input === null || input === undefined) return input;
|
|
48
71
|
|
|
49
|
-
|
|
72
|
+
if (Array.isArray(input)) {
|
|
73
|
+
const arr: any[] = [];
|
|
74
|
+
for (let i = 0; i < input.length; i++) {
|
|
75
|
+
arr[i] = deepDecrypt(input[i]);
|
|
76
|
+
}
|
|
77
|
+
return arr;
|
|
78
|
+
}
|
|
50
79
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
80
|
+
if (typeof input === "object") {
|
|
81
|
+
const out: { [k: string]: any } = {};
|
|
82
|
+
for (const k in input) {
|
|
83
|
+
const v = input[k];
|
|
84
|
+
if (k.length >= 10 && k.substr(0, 10) === "encrypted_") {
|
|
85
|
+
try {
|
|
86
|
+
out[k] = decryptValue(v);
|
|
87
|
+
} catch {
|
|
88
|
+
out[k] = v; // si está corrupto, lo dejamos como está
|
|
89
|
+
}
|
|
54
90
|
} else {
|
|
55
|
-
|
|
91
|
+
out[k] = deepDecrypt(v);
|
|
56
92
|
}
|
|
57
93
|
}
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// primitivos
|
|
98
|
+
return input;
|
|
99
|
+
}
|
|
58
100
|
|
|
59
|
-
|
|
101
|
+
export const JsonEncryptionTransformer: ValueTransformer = {
|
|
102
|
+
to(value: any): string {
|
|
103
|
+
if (typeof value !== "object" || value === null) return JSON.stringify({});
|
|
104
|
+
return JSON.stringify(deepEncrypt(value));
|
|
60
105
|
},
|
|
61
106
|
|
|
62
107
|
from(dbValue: string): any {
|
|
63
108
|
try {
|
|
64
109
|
const parsed = JSON.parse(dbValue);
|
|
65
|
-
|
|
66
|
-
if (key.startsWith("encrypted_")) {
|
|
67
|
-
parsed[key] = decryptValue(parsed[key]);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return parsed;
|
|
110
|
+
return deepDecrypt(parsed);
|
|
71
111
|
} catch {
|
|
112
|
+
// si no es JSON válido, no tumbar todo
|
|
72
113
|
return {};
|
|
73
114
|
}
|
|
74
115
|
},
|