@oondemand/oon-core-back 0.7.2 → 0.7.4
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/README.md +18 -0
- package/package.json +1 -1
- package/src/core/define.js +3 -0
- package/src/core/middlewares/requestLog.js +4 -2
- package/src/core/registry.js +37 -0
- package/src/delivery/runtime.js +2 -0
package/README.md
CHANGED
|
@@ -73,3 +73,21 @@ defineModel({
|
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
Use `populateRefs: true` para todas as referências da model ou informe uma lista explícita. O CRUD devolve somente `_id` e campos usuais de identificação, como nome, razão social, descrição, código, e-mail e campos pesquisáveis da model referenciada. Exportações mantêm os identificadores originais.
|
|
76
|
+
|
|
77
|
+
## Requisições com corpo sensível
|
|
78
|
+
|
|
79
|
+
Rotas privadas declaradas com `defineRoutes` que recebem credenciais ou outros segredos devem marcar a operação mutante com `sensitiveBody: true`:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
defineRoutes("/integracao", (router) => {
|
|
83
|
+
router.private.put("/credencial", {
|
|
84
|
+
permission: "integracao.config.write",
|
|
85
|
+
sensitiveBody: true,
|
|
86
|
+
}, async (req, res) => {
|
|
87
|
+
// Validar e persistir com segurança; devolver somente metadados sanitizados.
|
|
88
|
+
res.json({ configured: true });
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
O Core omite todo o corpo de entrada e resposta no `requestLog` para esse método e caminho, inclusive se a rota responder erro. Método, URL, status, usuário e correlationId continuam registrados. A opção não cifra o banco nem substitui autorização, validação e respostas sanitizadas. Nunca retorne o segredo pela rota, pelo erro ou pela auditoria da mutação. A opção não se aplica automaticamente a endpoints de CRUD gerados.
|
package/package.json
CHANGED
package/src/core/define.js
CHANGED
|
@@ -161,6 +161,9 @@ function defineRoutes(basePath, builder) {
|
|
|
161
161
|
const required = opts.permission || opts.permissions;
|
|
162
162
|
if (required) chain.push(requirePermission(required));
|
|
163
163
|
else chain.push(rbac(opts.roles || []));
|
|
164
|
+
if (["post", "put", "patch", "delete"].includes(method) && opts.sensitiveBody === true) {
|
|
165
|
+
registry.registerSensitiveRequest(method, basePath, path);
|
|
166
|
+
}
|
|
164
167
|
if (["post", "put", "patch", "delete"].includes(method) && opts.audit) {
|
|
165
168
|
chain.push(mutationAudit(opts.audit));
|
|
166
169
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const Log = require("../models/Log");
|
|
2
|
+
const registry = require("../registry");
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Registra requisições mutantes (não-GET) na model interna Log.
|
|
@@ -7,18 +8,19 @@ const Log = require("../models/Log");
|
|
|
7
8
|
const requestLog = async (req, res, next) => {
|
|
8
9
|
if (req.method === "GET") return next();
|
|
9
10
|
|
|
11
|
+
const sensitiveBody = await registry.isSensitiveRequest(req.method, req.url || req.originalUrl);
|
|
10
12
|
const log = new Log({
|
|
11
13
|
usuario: req.usuario || null,
|
|
12
14
|
endpoint: req.originalUrl,
|
|
13
15
|
metodo: req.method,
|
|
14
16
|
ip: req.ip || req.headers["x-forwarded-for"] || req.connection?.remoteAddress,
|
|
15
17
|
correlationId: req.correlationId,
|
|
16
|
-
dadosRequisicao: req.body,
|
|
18
|
+
dadosRequisicao: sensitiveBody ? { omitted: true } : req.body,
|
|
17
19
|
});
|
|
18
20
|
|
|
19
21
|
res.on("finish", () => {
|
|
20
22
|
log.statusResposta = res.statusCode;
|
|
21
|
-
log.dadosResposta = res.locals.body || null;
|
|
23
|
+
log.dadosResposta = sensitiveBody ? null : (res.locals.body || null);
|
|
22
24
|
log.save().catch(() => {});
|
|
23
25
|
});
|
|
24
26
|
|
package/src/core/registry.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const express = require("express");
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Model Registry — coração técnico do Core (Seção 17).
|
|
3
5
|
*
|
|
@@ -17,6 +19,7 @@ const store = {
|
|
|
17
19
|
pipelines: new Map(),
|
|
18
20
|
menus: [],
|
|
19
21
|
features: new Map(),
|
|
22
|
+
sensitiveRequests: [], // Express routers used only to classify sensitive paths
|
|
20
23
|
};
|
|
21
24
|
|
|
22
25
|
function registerModel(entry) {
|
|
@@ -68,6 +71,37 @@ const simpleRegister = (mapName) => (key, value) => {
|
|
|
68
71
|
};
|
|
69
72
|
const simpleList = (mapName) => () => [...store[mapName].values()];
|
|
70
73
|
|
|
74
|
+
function registerSensitiveRequest(method, basePath, path) {
|
|
75
|
+
// Use the same mount and route semantics as defineRoutes/createApp.
|
|
76
|
+
const route = express.Router();
|
|
77
|
+
route[method.toLowerCase()](path, (req) => req.sensitiveMatch());
|
|
78
|
+
const mounted = express.Router();
|
|
79
|
+
mounted.use(basePath, route);
|
|
80
|
+
store.sensitiveRequests.push(mounted);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function isSensitiveRequest(method, url) {
|
|
84
|
+
for (const router of store.sensitiveRequests) {
|
|
85
|
+
const matched = await new Promise((resolve) => {
|
|
86
|
+
// A separate request prevents classification from modifying the real request.
|
|
87
|
+
const request = {
|
|
88
|
+
method: String(method).toUpperCase(),
|
|
89
|
+
url: String(url || '/'),
|
|
90
|
+
headers: {},
|
|
91
|
+
sensitiveMatch: () => resolve(true),
|
|
92
|
+
};
|
|
93
|
+
// Routing errors fail closed: omit bodies instead of risking disclosure.
|
|
94
|
+
try {
|
|
95
|
+
router.handle(request, {}, (error) => resolve(Boolean(error)));
|
|
96
|
+
} catch {
|
|
97
|
+
resolve(true);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
if (matched) return true;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
71
105
|
function registerMenu(item) {
|
|
72
106
|
store.menus.push(item);
|
|
73
107
|
}
|
|
@@ -82,6 +116,7 @@ function reset() {
|
|
|
82
116
|
store.pipelines.clear();
|
|
83
117
|
store.menus.length = 0;
|
|
84
118
|
store.features.clear();
|
|
119
|
+
store.sensitiveRequests.length = 0;
|
|
85
120
|
}
|
|
86
121
|
|
|
87
122
|
module.exports = {
|
|
@@ -104,6 +139,8 @@ module.exports = {
|
|
|
104
139
|
registerFeature: simpleRegister("features"),
|
|
105
140
|
listFeatures: simpleList("features"),
|
|
106
141
|
registerMenu,
|
|
142
|
+
registerSensitiveRequest,
|
|
143
|
+
isSensitiveRequest,
|
|
107
144
|
listMenus: () => store.menus,
|
|
108
145
|
reset,
|
|
109
146
|
};
|
package/src/delivery/runtime.js
CHANGED
|
@@ -164,6 +164,8 @@ function applyRuntimeSecret({ context, mongoUri, encryptionKey, extraSecrets = {
|
|
|
164
164
|
NODE_ENV: "production",
|
|
165
165
|
APP_ENVIRONMENT: context.environment,
|
|
166
166
|
APP_CODE: context.appCode,
|
|
167
|
+
...(context.commitSha ? { OON_BUILD_COMMIT: context.commitSha } : {}),
|
|
168
|
+
OON_RELEASE_ID: context.releaseId,
|
|
167
169
|
PUBLIC_APP_URL: context.publicUrl,
|
|
168
170
|
CENTRAL_ATIVACAO_URL: context.activationApiUrl,
|
|
169
171
|
CENTRAL_ATIVACAO_API_URL: context.activationApiUrl,
|