@vulkano/core 1.24.0 → 1.24.1
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/database/scaffold.js +84 -1
- package/package.json +1 -1
- package/views/helpers/i18n.js +8 -0
package/database/scaffold.js
CHANGED
|
@@ -1,5 +1,67 @@
|
|
|
1
|
+
// Field names that are never exposed through populate select, whatever the
|
|
2
|
+
// caller asks for — a coarse, name-based backstop for common secret-shaped
|
|
3
|
+
// fields (defense in depth on top of the schema's own `select: false`, in
|
|
4
|
+
// case a referenced model forgets to mark a sensitive field itself).
|
|
5
|
+
const SENSITIVE_FIELD_HINTS = [
|
|
6
|
+
'password', 'passwd', 'secret', 'token', 'apikey', 'api_key',
|
|
7
|
+
'privatekey', 'private_key', 'hash', 'salt', 'ssn', 'creditcard',
|
|
8
|
+
'credit_card', 'cvv', 'pin'
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
function isSensitiveByName(field) {
|
|
12
|
+
const lower = field.toLowerCase();
|
|
13
|
+
return SENSITIVE_FIELD_HINTS.some((hint) => lower.includes(hint));
|
|
14
|
+
}
|
|
15
|
+
|
|
1
16
|
module.exports = {
|
|
2
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Look up the referenced model on the same connection this model was
|
|
20
|
+
* compiled on (models here are registered via `db.model(...)`, not the
|
|
21
|
+
* global `mongoose.model(...)` registry — see database/mongodb.js — so
|
|
22
|
+
* resolving by connection is required for this to ever find it).
|
|
23
|
+
*
|
|
24
|
+
* @param {String} refModelName mongoose ref (the "ref" schema option)
|
|
25
|
+
* @returns {Object|null}
|
|
26
|
+
*/
|
|
27
|
+
_resolveRefModel(refModelName) {
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
return (this.db && this.db.model(refModelName)) || mongoose.model(refModelName);
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A field is safe to expose through populate select when it's neither
|
|
39
|
+
* name-flagged as sensitive (SENSITIVE_FIELD_HINTS) nor marked
|
|
40
|
+
* `select: false` on the REFERENCED model's own schema. If the referenced
|
|
41
|
+
* model can't be resolved (e.g. in isolated unit tests), only the
|
|
42
|
+
* name-based check applies.
|
|
43
|
+
*
|
|
44
|
+
* @param {String} refModelName mongoose ref (the "ref" schema option)
|
|
45
|
+
* @param {String} field
|
|
46
|
+
* @returns {Boolean}
|
|
47
|
+
*/
|
|
48
|
+
_isFieldSafeToExpose(refModelName, field) {
|
|
49
|
+
|
|
50
|
+
if (isSensitiveByName(field)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const RefModel = this._resolveRefModel(refModelName);
|
|
55
|
+
const refPath = RefModel && RefModel.schema.paths[field];
|
|
56
|
+
|
|
57
|
+
if (refPath && refPath.options && refPath.options.select === false) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return true;
|
|
62
|
+
|
|
63
|
+
},
|
|
64
|
+
|
|
3
65
|
/**
|
|
4
66
|
* Parse the populate param into one entry per relation: `name` (trimmed,
|
|
5
67
|
* lowercased) plus an optional `fields` list. Syntax: relations are
|
|
@@ -103,7 +165,28 @@ module.exports = {
|
|
|
103
165
|
const populateProps = { path: field };
|
|
104
166
|
|
|
105
167
|
if (entry.fields && entry.fields.length > 0) {
|
|
106
|
-
|
|
168
|
+
|
|
169
|
+
// Explicit field list: keep only what's safe, as a pure inclusion
|
|
170
|
+
// select. If everything requested turns out sensitive, fall back
|
|
171
|
+
// to "_id" only — never to the unfiltered full document.
|
|
172
|
+
const safeFields = entry.fields.filter((f) => this._isFieldSafeToExpose(ref, f));
|
|
173
|
+
populateProps.select = safeFields.length > 0 ? safeFields.join(' ') : '_id';
|
|
174
|
+
|
|
175
|
+
} else {
|
|
176
|
+
|
|
177
|
+
// Full-doc populate: still must not leak sensitive fields, so
|
|
178
|
+
// build an exclusion select for whatever the ref schema/name-hints
|
|
179
|
+
// flag on that model — skipped only if nothing needs excluding.
|
|
180
|
+
const RefModel = this._resolveRefModel(ref);
|
|
181
|
+
|
|
182
|
+
const toExclude = RefModel
|
|
183
|
+
? Object.keys(RefModel.schema.paths).filter((f) => !this._isFieldSafeToExpose(ref, f))
|
|
184
|
+
: [];
|
|
185
|
+
|
|
186
|
+
if (toExclude.length > 0) {
|
|
187
|
+
populateProps.select = toExclude.map((f) => `-${f}`).join(' ');
|
|
188
|
+
}
|
|
189
|
+
|
|
107
190
|
}
|
|
108
191
|
|
|
109
192
|
return populateProps;
|
package/package.json
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridges the global `i18n` instance (set by bootstrap/services.js) into the
|
|
3
|
+
* Nunjucks environment. Without this, `i18n` exists only in JS scope — views
|
|
4
|
+
* never receive it, since nunjucks.js only auto-adds `app` as a global.
|
|
5
|
+
*
|
|
6
|
+
* Usage in templates: {{ i18n.t('key') }}
|
|
7
|
+
*/
|
|
8
|
+
module.exports = i18n;
|