@semacode/cli 1.2.0 → 1.2.11
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 +2 -2
- package/SEMA_BRIEF.curto.txt +9 -9
- package/SEMA_BRIEF.md +49 -49
- package/SEMA_BRIEF.micro.txt +7 -7
- package/SEMA_INDEX.json +501 -546
- package/dist/drift.d.ts +3 -3
- package/dist/drift.js +213 -22
- package/dist/drift.js.map +1 -1
- package/dist/importador.d.ts +1 -1
- package/dist/importador.js +0 -60
- package/dist/importador.js.map +1 -1
- package/dist/index.js +1334 -1360
- package/dist/index.js.map +1 -1
- package/dist/projeto.js +0 -6
- package/dist/projeto.js.map +1 -1
- package/dist/tipos.d.ts +1 -1
- package/docs/AGENT_STARTER.md +102 -102
- package/docs/instalacao-e-primeiro-uso.md +196 -198
- package/docs/sintaxe.md +95 -1
- package/node_modules/@sema/gerador-dart/package.json +1 -1
- package/node_modules/@sema/gerador-lua/dist/index.js +49 -81
- package/node_modules/@sema/gerador-lua/dist/index.js.map +1 -1
- package/node_modules/@sema/gerador-lua/package.json +1 -1
- package/node_modules/@sema/gerador-python/package.json +1 -1
- package/node_modules/@sema/gerador-typescript/package.json +1 -1
- package/node_modules/@sema/nucleo/dist/ast/tipos.d.ts +8 -2
- package/node_modules/@sema/nucleo/dist/formatador/index.js +65 -21
- package/node_modules/@sema/nucleo/dist/formatador/index.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/ir/conversor.js +91 -5
- package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +74 -3
- package/node_modules/@sema/nucleo/dist/lexer/tokens.js +6 -0
- package/node_modules/@sema/nucleo/dist/lexer/tokens.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/parser/parser.js +12 -2
- package/node_modules/@sema/nucleo/dist/parser/parser.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/semantico/analisador.d.ts +2 -2
- package/node_modules/@sema/nucleo/dist/semantico/analisador.js +410 -11
- package/node_modules/@sema/nucleo/dist/semantico/analisador.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/semantico/estruturas.d.ts +6 -1
- package/node_modules/@sema/nucleo/dist/semantico/estruturas.js +63 -13
- package/node_modules/@sema/nucleo/dist/semantico/estruturas.js.map +1 -1
- package/node_modules/@sema/nucleo/dist/semantico/seguranca.d.ts +91 -0
- package/node_modules/@sema/nucleo/dist/semantico/seguranca.js +258 -0
- package/node_modules/@sema/nucleo/dist/semantico/seguranca.js.map +1 -0
- package/node_modules/@sema/nucleo/package.json +1 -1
- package/node_modules/@sema/padroes/dist/index.js +18 -1
- package/node_modules/@sema/padroes/dist/index.js.map +1 -1
- package/node_modules/@sema/padroes/package.json +1 -1
- package/package.json +7 -7
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { criarDiagnostico } from "../diagnosticos/index.js";
|
|
2
2
|
import { ehCategoriaEfeitoSemantico, ehCriticidadeEfeitoSemantico, extrairReferenciasDaExpressao, parsearEfeitoSemantico, parsearEtapaFlow, parsearExpressaoSemantica, parsearTransicaoEstado, } from "./estruturas.js";
|
|
3
|
+
import { CLASSIFICACOES_DADO_SUPORTADAS, MODOS_AUTH_SUPORTADOS, MOTIVOS_AUDIT_SUPORTADOS, ORIGENS_AUTH_SUPORTADAS, PRINCIPAIS_AUTH_SUPORTADOS, REDACOES_LOG_SUPORTADAS, TENANTS_AUTHZ_SUPORTADOS, contratoDadosTemSegredoOuCredencial, contratoDadosTemSensivel, extrairContratoAudit, extrairContratoAuth, extrairContratoAuthz, extrairContratoDados, extrairContratoForbidden, extrairContratoSegredos, efeitoEhPrivilegiado, efeitoRequerSegredo, forbiddenContemRegra, } from "./seguranca.js";
|
|
3
4
|
function ehUseInterop(use) {
|
|
4
5
|
return use.origem !== "sema";
|
|
5
6
|
}
|
|
@@ -44,6 +45,41 @@ const CAMPOS_EXECUCAO_SUPORTADOS = new Set([
|
|
|
44
45
|
"compensacao",
|
|
45
46
|
"criticidade_operacional",
|
|
46
47
|
]);
|
|
48
|
+
const CAMPOS_AUTH_SUPORTADOS = new Set([
|
|
49
|
+
"modo",
|
|
50
|
+
"estrategia",
|
|
51
|
+
"principal",
|
|
52
|
+
"origem",
|
|
53
|
+
]);
|
|
54
|
+
const CAMPOS_AUTHZ_SUPORTADOS = new Set([
|
|
55
|
+
"papel",
|
|
56
|
+
"papeis",
|
|
57
|
+
"escopo",
|
|
58
|
+
"escopos",
|
|
59
|
+
"politica",
|
|
60
|
+
"tenant",
|
|
61
|
+
]);
|
|
62
|
+
const CAMPOS_DADOS_SUPORTADOS = new Set([
|
|
63
|
+
"classificacao_padrao",
|
|
64
|
+
"redacao_log",
|
|
65
|
+
"retencao",
|
|
66
|
+
]);
|
|
67
|
+
const CAMPOS_AUDIT_SUPORTADOS = new Set([
|
|
68
|
+
"evento",
|
|
69
|
+
"ator",
|
|
70
|
+
"correlacao",
|
|
71
|
+
"retencao",
|
|
72
|
+
"motivo",
|
|
73
|
+
]);
|
|
74
|
+
const CAMPOS_SEGREDO_SUPORTADOS = new Set([
|
|
75
|
+
"origem",
|
|
76
|
+
"escopo",
|
|
77
|
+
"acesso",
|
|
78
|
+
"rotacao",
|
|
79
|
+
"nao_logar",
|
|
80
|
+
"nao_retornar",
|
|
81
|
+
"mascarar",
|
|
82
|
+
]);
|
|
47
83
|
const CAMPOS_ERRO_OPERACIONAL = new Set([
|
|
48
84
|
"mensagem",
|
|
49
85
|
"categoria",
|
|
@@ -64,8 +100,6 @@ function normalizarOrigemImplementacao(valor) {
|
|
|
64
100
|
return "py";
|
|
65
101
|
case "dart":
|
|
66
102
|
return "dart";
|
|
67
|
-
case "lua":
|
|
68
|
-
return "lua";
|
|
69
103
|
case "cs":
|
|
70
104
|
case "csharp":
|
|
71
105
|
case "dotnet":
|
|
@@ -111,9 +145,15 @@ function validarCamposDeTipos(campos, tiposConhecidos, diagnosticos, contexto) {
|
|
|
111
145
|
}
|
|
112
146
|
}
|
|
113
147
|
function localizarBloco(corpo, nome) {
|
|
114
|
-
|
|
148
|
+
if (!corpo) {
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
return corpo.blocos.find((bloco) => bloco.tipo === "bloco_generico" && (bloco.palavraChave === nome || bloco.nome === nome));
|
|
115
152
|
}
|
|
116
153
|
function localizarCampo(bloco, ...nomes) {
|
|
154
|
+
if (!bloco) {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
117
157
|
return bloco.campos.find((campo) => nomes.includes(campo.nome));
|
|
118
158
|
}
|
|
119
159
|
function valorCampoCompleto(campo) {
|
|
@@ -209,7 +249,7 @@ function validarImplementacoesTask(task, diagnosticos) {
|
|
|
209
249
|
for (const campo of task.impl.campos) {
|
|
210
250
|
const origem = normalizarOrigemImplementacao(campo.nome);
|
|
211
251
|
if (!origem) {
|
|
212
|
-
diagnosticos.push(criarDiagnostico("SEM059", `Task "${task.nome}" declarou implementacao externa invalida em impl: "${campo.nome}".`, "erro", campo.intervalo, "Use apenas ts, py, dart,
|
|
252
|
+
diagnosticos.push(criarDiagnostico("SEM059", `Task "${task.nome}" declarou implementacao externa invalida em impl: "${campo.nome}".`, "erro", campo.intervalo, "Use apenas ts, py, dart, cs, java, go, rust ou cpp dentro do bloco impl."));
|
|
213
253
|
continue;
|
|
214
254
|
}
|
|
215
255
|
if (origens.has(origem)) {
|
|
@@ -232,23 +272,280 @@ function validarVinculos(bloco, diagnosticos, contexto) {
|
|
|
232
272
|
}
|
|
233
273
|
}
|
|
234
274
|
}
|
|
235
|
-
function
|
|
236
|
-
|
|
275
|
+
function extrairPerfilCompatibilidade(bloco, padrao = "interno") {
|
|
276
|
+
const perfil = bloco
|
|
277
|
+
? valorCampoCompleto(localizarCampo(bloco, "perfil", "compatibilidade"))?.toLowerCase()
|
|
278
|
+
: undefined;
|
|
279
|
+
if (perfil === "publico"
|
|
280
|
+
|| perfil === "interno"
|
|
281
|
+
|| perfil === "experimental"
|
|
282
|
+
|| perfil === "legado"
|
|
283
|
+
|| perfil === "deprecado") {
|
|
284
|
+
return perfil;
|
|
285
|
+
}
|
|
286
|
+
return padrao;
|
|
287
|
+
}
|
|
288
|
+
function coletarSuperficiesModulo(modulo) {
|
|
289
|
+
return [
|
|
290
|
+
...modulo.workers.map((superficie) => ({ tipo: "worker", superficie })),
|
|
291
|
+
...modulo.eventos.map((superficie) => ({ tipo: "evento", superficie })),
|
|
292
|
+
...modulo.filas.map((superficie) => ({ tipo: "fila", superficie })),
|
|
293
|
+
...modulo.crons.map((superficie) => ({ tipo: "cron", superficie })),
|
|
294
|
+
...modulo.webhooks.map((superficie) => ({ tipo: "webhook", superficie })),
|
|
295
|
+
...modulo.caches.map((superficie) => ({ tipo: "cache", superficie })),
|
|
296
|
+
...modulo.storages.map((superficie) => ({ tipo: "storage", superficie })),
|
|
297
|
+
...modulo.policies.map((superficie) => ({ tipo: "policy", superficie })),
|
|
298
|
+
];
|
|
299
|
+
}
|
|
300
|
+
function superficieEhPublica(superficie, tipoSuperficie) {
|
|
301
|
+
return extrairPerfilCompatibilidade(superficie, tipoSuperficie === "webhook" ? "publico" : "interno") === "publico";
|
|
302
|
+
}
|
|
303
|
+
function taskEhSensivel(task) {
|
|
304
|
+
const criticidadeOperacional = task.execucao
|
|
305
|
+
? valorCampoCompleto(localizarCampo(task.execucao, "criticidade_operacional"))
|
|
306
|
+
: undefined;
|
|
307
|
+
if (criticidadeOperacional === "alta" || criticidadeOperacional === "critica") {
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
return (task.effects?.linhas ?? []).some((linha) => {
|
|
311
|
+
const efeito = parsearEfeitoSemantico(linha.conteudo);
|
|
312
|
+
if (!efeito) {
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
return efeito.categoria === "persistencia" || efeito.criticidade === "alta" || efeito.criticidade === "critica" || efeitoEhPrivilegiado(efeito);
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
function taskTemRastreabilidade(task) {
|
|
319
|
+
return Boolean(task.impl || task.vinculos);
|
|
320
|
+
}
|
|
321
|
+
function routeEhMutante(route) {
|
|
322
|
+
const metodo = (localizarCampo(route.corpo, "metodo")?.valor ?? "").toUpperCase();
|
|
323
|
+
return ["POST", "PUT", "PATCH", "DELETE"].includes(metodo);
|
|
324
|
+
}
|
|
325
|
+
function validarExecucaoBloco(execucao, diagnosticos, contexto) {
|
|
326
|
+
if (!execucao) {
|
|
237
327
|
return;
|
|
238
328
|
}
|
|
239
|
-
for (const campo of
|
|
329
|
+
for (const campo of execucao.campos) {
|
|
240
330
|
if (!CAMPOS_EXECUCAO_SUPORTADOS.has(campo.nome)) {
|
|
241
|
-
diagnosticos.push(criarDiagnostico("SEM065", `Campo de execucao "${campo.nome}" nao e suportado
|
|
331
|
+
diagnosticos.push(criarDiagnostico("SEM065", `Campo de execucao "${campo.nome}" nao e suportado em ${contexto}.`, "erro", campo.intervalo, "Use apenas idempotencia, timeout, retry, compensacao ou criticidade_operacional."));
|
|
242
332
|
continue;
|
|
243
333
|
}
|
|
244
334
|
if (campo.nome === "criticidade_operacional") {
|
|
245
335
|
const criticidade = valorCampoCompleto(campo);
|
|
246
336
|
if (criticidade && !CRITICIDADES_OPERACIONAIS.has(criticidade)) {
|
|
247
|
-
diagnosticos.push(criarDiagnostico("SEM066", `
|
|
337
|
+
diagnosticos.push(criarDiagnostico("SEM066", `Execucao de ${contexto} declarou criticidade_operacional invalida: "${criticidade}".`, "erro", campo.intervalo, "Use apenas baixa, media, alta ou critica em execucao."));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function validarExecucao(task, diagnosticos) {
|
|
343
|
+
validarExecucaoBloco(task.execucao, diagnosticos, `task "${task.nome}"`);
|
|
344
|
+
}
|
|
345
|
+
function validarAuthBloco(bloco, diagnosticos, contexto) {
|
|
346
|
+
if (!bloco) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
for (const campo of bloco.campos) {
|
|
350
|
+
if (!CAMPOS_AUTH_SUPORTADOS.has(campo.nome)) {
|
|
351
|
+
diagnosticos.push(criarDiagnostico("SEM074", `Campo de auth "${campo.nome}" nao e suportado em ${contexto}.`, "erro", campo.intervalo, "Use apenas modo, estrategia, principal ou origem em auth."));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const auth = extrairContratoAuth(bloco);
|
|
355
|
+
if (auth.modo && !MODOS_AUTH_SUPORTADOS.has(auth.modo)) {
|
|
356
|
+
diagnosticos.push(criarDiagnostico("SEM075", `Auth em ${contexto} declarou modo invalido: "${auth.modo}".`, "erro", bloco.intervalo, "Use obrigatorio, opcional, anonimo, interno ou m2m."));
|
|
357
|
+
}
|
|
358
|
+
if (auth.principal && !PRINCIPAIS_AUTH_SUPORTADOS.has(auth.principal)) {
|
|
359
|
+
diagnosticos.push(criarDiagnostico("SEM076", `Auth em ${contexto} declarou principal invalido: "${auth.principal}".`, "erro", bloco.intervalo, "Use usuario, servico, sistema ou anonimo."));
|
|
360
|
+
}
|
|
361
|
+
if (auth.origem && !ORIGENS_AUTH_SUPORTADAS.has(auth.origem)) {
|
|
362
|
+
diagnosticos.push(criarDiagnostico("SEM077", `Auth em ${contexto} declarou origem invalida: "${auth.origem}".`, "erro", bloco.intervalo, "Use publica, interna, worker, webhook, fila ou cron."));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
function validarAuthzBloco(bloco, diagnosticos, contexto) {
|
|
366
|
+
if (!bloco) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
for (const campo of bloco.campos) {
|
|
370
|
+
if (!CAMPOS_AUTHZ_SUPORTADOS.has(campo.nome)) {
|
|
371
|
+
diagnosticos.push(criarDiagnostico("SEM078", `Campo de authz "${campo.nome}" nao e suportado em ${contexto}.`, "erro", campo.intervalo, "Use papel, papeis, escopo, escopos, politica ou tenant em authz."));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
const authz = extrairContratoAuthz(bloco);
|
|
375
|
+
if (authz.tenant && !TENANTS_AUTHZ_SUPORTADOS.has(authz.tenant)) {
|
|
376
|
+
diagnosticos.push(criarDiagnostico("SEM079", `Authz em ${contexto} declarou tenant invalido: "${authz.tenant}".`, "erro", bloco.intervalo, "Use obrigatorio, opcional ou isolado."));
|
|
377
|
+
}
|
|
378
|
+
if (authz.papeis.length === 0 && authz.escopos.length === 0 && !authz.politica) {
|
|
379
|
+
diagnosticos.push(criarDiagnostico("SEM080", `Authz em ${contexto} precisa declarar papeis, escopos ou politica.`, "erro", bloco.intervalo, "Explicite ao menos um papel, escopo ou politica para a autorizacao nao virar enfeite."));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
function validarDadosBloco(bloco, diagnosticos, contexto) {
|
|
383
|
+
if (!bloco) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
for (const campo of bloco.campos) {
|
|
387
|
+
const valor = valorCampoCompleto(campo);
|
|
388
|
+
if (CAMPOS_DADOS_SUPORTADOS.has(campo.nome)) {
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (valor && !CLASSIFICACOES_DADO_SUPORTADAS.has(valor)) {
|
|
392
|
+
diagnosticos.push(criarDiagnostico("SEM081", `Dados em ${contexto} declarou classificacao invalida para "${campo.nome}": "${valor}".`, "erro", campo.intervalo, "Use publico, interno, pii, financeiro, credencial ou segredo."));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
const dados = extrairContratoDados(bloco);
|
|
396
|
+
if (dados.classificacaoPadrao && !CLASSIFICACOES_DADO_SUPORTADAS.has(dados.classificacaoPadrao)) {
|
|
397
|
+
diagnosticos.push(criarDiagnostico("SEM081", `Dados em ${contexto} declarou classificacao_padrao invalida: "${dados.classificacaoPadrao}".`, "erro", bloco.intervalo, "Use publico, interno, pii, financeiro, credencial ou segredo."));
|
|
398
|
+
}
|
|
399
|
+
if (dados.redacaoLog && !REDACOES_LOG_SUPORTADAS.has(dados.redacaoLog)) {
|
|
400
|
+
diagnosticos.push(criarDiagnostico("SEM082", `Dados em ${contexto} declarou redacao_log invalida: "${dados.redacaoLog}".`, "erro", bloco.intervalo, "Use livre, parcial, obrigatoria ou proibida."));
|
|
401
|
+
}
|
|
402
|
+
for (const subbloco of bloco.blocos) {
|
|
403
|
+
if (subbloco.tipo !== "bloco_generico") {
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
const nomeSubbloco = subbloco.nome ?? subbloco.palavraChave;
|
|
407
|
+
if (nomeSubbloco !== "input" && nomeSubbloco !== "output") {
|
|
408
|
+
diagnosticos.push(criarDiagnostico("SEM083", `Dados em ${contexto} nao suporta o subbloco "${nomeSubbloco}".`, "erro", subbloco.intervalo, "Use apenas campos diretos ou subblocos input/output para classificar dados."));
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
for (const campo of subbloco.campos) {
|
|
412
|
+
const classificacao = valorCampoCompleto(campo);
|
|
413
|
+
if (classificacao && !CLASSIFICACOES_DADO_SUPORTADAS.has(classificacao)) {
|
|
414
|
+
diagnosticos.push(criarDiagnostico("SEM081", `Dados em ${contexto} declarou classificacao invalida para "${nomeSubbloco}.${campo.nome}": "${classificacao}".`, "erro", campo.intervalo, "Use publico, interno, pii, financeiro, credencial ou segredo."));
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function validarAuditBloco(bloco, diagnosticos, contexto) {
|
|
420
|
+
if (!bloco) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
for (const campo of bloco.campos) {
|
|
424
|
+
if (!CAMPOS_AUDIT_SUPORTADOS.has(campo.nome)) {
|
|
425
|
+
diagnosticos.push(criarDiagnostico("SEM084", `Campo de audit "${campo.nome}" nao e suportado em ${contexto}.`, "erro", campo.intervalo, "Use evento, ator, correlacao, retencao ou motivo em audit."));
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
const audit = extrairContratoAudit(bloco);
|
|
429
|
+
if (!audit.evento) {
|
|
430
|
+
diagnosticos.push(criarDiagnostico("SEM085", `Audit em ${contexto} precisa declarar evento.`, "erro", bloco.intervalo, "Explique qual evento auditavel sera registrado para a operacao."));
|
|
431
|
+
}
|
|
432
|
+
if (audit.motivo && !MOTIVOS_AUDIT_SUPORTADOS.has(audit.motivo)) {
|
|
433
|
+
diagnosticos.push(criarDiagnostico("SEM086", `Audit em ${contexto} declarou motivo invalido: "${audit.motivo}".`, "erro", bloco.intervalo, "Use obrigatorio, opcional ou dispensado."));
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
function validarSegredosBloco(bloco, diagnosticos, contexto) {
|
|
437
|
+
if (!bloco) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
const segredos = extrairContratoSegredos(bloco);
|
|
441
|
+
if (segredos.itens.length === 0) {
|
|
442
|
+
diagnosticos.push(criarDiagnostico("SEM087", `Segredos em ${contexto} precisa declarar ao menos um segredo nomeado.`, "erro", bloco.intervalo, "Use segredos { nome_do_segredo { origem: vault escopo: runtime ... } }."));
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
for (const item of bloco.blocos) {
|
|
446
|
+
if (item.tipo !== "bloco_generico") {
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
for (const campo of item.campos) {
|
|
450
|
+
if (!CAMPOS_SEGREDO_SUPORTADOS.has(campo.nome)) {
|
|
451
|
+
diagnosticos.push(criarDiagnostico("SEM087", `Segredo "${item.nome ?? item.palavraChave}" em ${contexto} usa o campo "${campo.nome}", que nao e suportado.`, "erro", campo.intervalo, "Use origem, escopo, acesso, rotacao, nao_logar, nao_retornar ou mascarar."));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
const nomeSegredo = item.nome ?? item.palavraChave;
|
|
455
|
+
const origem = valorCampoCompleto(localizarCampo(item, "origem"));
|
|
456
|
+
if (!origem) {
|
|
457
|
+
diagnosticos.push(criarDiagnostico("SEM088", `Segredo "${nomeSegredo}" em ${contexto} precisa declarar origem.`, "erro", item.intervalo, "Explicite a origem do segredo, como vault, env, secret_manager ou runtime."));
|
|
458
|
+
}
|
|
459
|
+
for (const nomeBooleano of ["nao_logar", "nao_retornar", "mascarar"]) {
|
|
460
|
+
const campo = localizarCampo(item, nomeBooleano);
|
|
461
|
+
const valor = valorCampoCompleto(campo);
|
|
462
|
+
if (campo && valor !== "verdadeiro" && valor !== "true" && valor !== "falso" && valor !== "false") {
|
|
463
|
+
diagnosticos.push(criarDiagnostico("SEM089", `Segredo "${nomeSegredo}" em ${contexto} declarou "${nomeBooleano}" com valor invalido: "${valor}".`, "erro", campo.intervalo, "Use verdadeiro/falso para campos booleanos de segredos."));
|
|
248
464
|
}
|
|
249
465
|
}
|
|
250
466
|
}
|
|
251
467
|
}
|
|
468
|
+
function validarForbiddenBloco(bloco, efeitos, diagnosticos, contexto) {
|
|
469
|
+
const forbidden = extrairContratoForbidden(bloco);
|
|
470
|
+
if (!bloco) {
|
|
471
|
+
return forbidden;
|
|
472
|
+
}
|
|
473
|
+
for (const regra of forbidden.regras) {
|
|
474
|
+
if (!/^[A-Za-z_][A-Za-z0-9_.-]*$/u.test(regra)) {
|
|
475
|
+
diagnosticos.push(criarDiagnostico("SEM090", `Forbidden em ${contexto} declarou regra invalida: "${regra}".`, "erro", bloco.intervalo, "Use regras simples como network.egress, shell.exec, retorno.credencial ou log.segredo."));
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
for (const linha of efeitos) {
|
|
479
|
+
const efeito = parsearEfeitoSemantico(linha.conteudo);
|
|
480
|
+
if (efeito && forbiddenContemRegra(forbidden, efeito.categoria)) {
|
|
481
|
+
diagnosticos.push(criarDiagnostico("SEM091", `Forbidden em ${contexto} proibe "${efeito.categoria}", mas effects ainda declara esse efeito.`, "erro", linha.intervalo, "Remova o efeito proibido ou ajuste o bloco forbidden para refletir a operacao permitida de verdade."));
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return forbidden;
|
|
485
|
+
}
|
|
486
|
+
function coletarPerfilSegurancaDeclarado(corpo, effects, diagnosticos, contexto) {
|
|
487
|
+
const authBloco = localizarBloco(corpo, "auth");
|
|
488
|
+
const authzBloco = localizarBloco(corpo, "authz");
|
|
489
|
+
const dadosBloco = localizarBloco(corpo, "dados");
|
|
490
|
+
const auditBloco = localizarBloco(corpo, "audit");
|
|
491
|
+
const segredosBloco = localizarBloco(corpo, "segredos");
|
|
492
|
+
const forbiddenBloco = localizarBloco(corpo, "forbidden");
|
|
493
|
+
if (diagnosticos) {
|
|
494
|
+
validarAuthBloco(authBloco, diagnosticos, contexto);
|
|
495
|
+
validarAuthzBloco(authzBloco, diagnosticos, contexto);
|
|
496
|
+
validarDadosBloco(dadosBloco, diagnosticos, contexto);
|
|
497
|
+
validarAuditBloco(auditBloco, diagnosticos, contexto);
|
|
498
|
+
validarSegredosBloco(segredosBloco, diagnosticos, contexto);
|
|
499
|
+
}
|
|
500
|
+
const forbidden = diagnosticos
|
|
501
|
+
? validarForbiddenBloco(forbiddenBloco, effects?.linhas ?? [], diagnosticos, contexto)
|
|
502
|
+
: extrairContratoForbidden(forbiddenBloco);
|
|
503
|
+
const auth = extrairContratoAuth(authBloco);
|
|
504
|
+
const authz = extrairContratoAuthz(authzBloco);
|
|
505
|
+
const dados = extrairContratoDados(dadosBloco);
|
|
506
|
+
const audit = extrairContratoAudit(auditBloco);
|
|
507
|
+
const segredos = extrairContratoSegredos(segredosBloco);
|
|
508
|
+
const efeitosEstruturados = (effects?.linhas ?? [])
|
|
509
|
+
.map((linha) => parsearEfeitoSemantico(linha.conteudo))
|
|
510
|
+
.filter((efeito) => Boolean(efeito));
|
|
511
|
+
return {
|
|
512
|
+
auth,
|
|
513
|
+
authz,
|
|
514
|
+
dados,
|
|
515
|
+
audit,
|
|
516
|
+
segredos,
|
|
517
|
+
forbidden,
|
|
518
|
+
efeitoPrivilegiado: efeitosEstruturados.some((efeito) => efeitoEhPrivilegiado(efeito)),
|
|
519
|
+
dadosSensiveis: contratoDadosTemSensivel(dados),
|
|
520
|
+
exigeSegredos: efeitosEstruturados.some((efeito) => efeitoRequerSegredo(efeito)) || contratoDadosTemSegredoOuCredencial(dados),
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
function emitirGuardrailsSeguranca(contexto, intervalo, perfil, diagnosticos, opcoes) {
|
|
524
|
+
const exigeAuth = opcoes.publico;
|
|
525
|
+
const exigeAuthz = opcoes.publico || opcoes.sensivel || perfil.efeitoPrivilegiado || perfil.dadosSensiveis;
|
|
526
|
+
const exigeDados = opcoes.publico || opcoes.sensivel || perfil.efeitoPrivilegiado;
|
|
527
|
+
const exigeAudit = opcoes.publico || opcoes.sensivel || perfil.efeitoPrivilegiado || perfil.dadosSensiveis;
|
|
528
|
+
const exigeSegredos = perfil.exigeSegredos;
|
|
529
|
+
const exigeForbidden = perfil.efeitoPrivilegiado || perfil.dadosSensiveis;
|
|
530
|
+
if (exigeAuth && !perfil.auth.explicita) {
|
|
531
|
+
diagnosticos.push(criarDiagnostico("SEM094", `${contexto} deveria declarar auth explicita para reduzir ambiguidade de seguranca na borda publica.`, "aviso", intervalo, "Declare auth { modo: obrigatorio|anonimo ... } para deixar a intencao da exposicao publica cristalina."));
|
|
532
|
+
}
|
|
533
|
+
if (exigeAuthz && !perfil.authz.explicita) {
|
|
534
|
+
diagnosticos.push(criarDiagnostico("SEM095", `${contexto} deveria declarar authz explicita porque opera com risco, privilegio ou exposicao publica.`, "aviso", intervalo, "Declare papeis, escopos ou politica em authz para nao empurrar autorizacao para o limbo do codigo vivo."));
|
|
535
|
+
}
|
|
536
|
+
if (exigeDados && !perfil.dados.explicita) {
|
|
537
|
+
diagnosticos.push(criarDiagnostico("SEM096", `${contexto} deveria classificar dados de forma explicita em dados { ... }.`, "aviso", intervalo, "Classifique input/output com publico, interno, pii, financeiro, credencial ou segredo."));
|
|
538
|
+
}
|
|
539
|
+
if (exigeAudit && !perfil.audit.explicita) {
|
|
540
|
+
diagnosticos.push(criarDiagnostico("SEM097", `${contexto} deveria declarar audit explicita para operar com trilha semantica de seguranca.`, "aviso", intervalo, "Declare audit { evento: ... correlacao: ... motivo: ... } para nao depender de adivinhacao operacional."));
|
|
541
|
+
}
|
|
542
|
+
if (exigeSegredos && !perfil.segredos.explicita) {
|
|
543
|
+
diagnosticos.push(criarDiagnostico("SEM098", `${contexto} deveria declarar segredos explicitos porque toca credencial, segredo ou secret.read.`, "aviso", intervalo, "Use segredos { nome { origem: vault escopo: runtime ... } } para governar acesso sensivel."));
|
|
544
|
+
}
|
|
545
|
+
if (exigeForbidden && !perfil.forbidden.explicita) {
|
|
546
|
+
diagnosticos.push(criarDiagnostico("SEM099", `${contexto} deveria declarar forbidden explicito para proibir operacoes perigosas ou vazamento semantico.`, "aviso", intervalo, "Use forbidden { network.egress shell.exec log.segredo retorno.credencial } conforme o risco da operacao."));
|
|
547
|
+
}
|
|
548
|
+
}
|
|
252
549
|
function validarErroOperacional(task, diagnosticos) {
|
|
253
550
|
if (!task.error) {
|
|
254
551
|
return;
|
|
@@ -291,6 +588,7 @@ function validarSuperficie(superficie, tipoSuperficie, tasksConhecidas, tiposCon
|
|
|
291
588
|
const effects = localizarBloco(superficie, "effects");
|
|
292
589
|
const impl = localizarBloco(superficie, "impl");
|
|
293
590
|
const vinculos = localizarBloco(superficie, "vinculos");
|
|
591
|
+
const execucao = localizarBloco(superficie, "execucao");
|
|
294
592
|
if (!task && !impl && !vinculos) {
|
|
295
593
|
diagnosticos.push(criarDiagnostico("SEM069", `Superficie ${tipoSuperficie} "${nomeSuperficie}" precisa declarar task, impl ou vinculos para nao virar bloco decorativo.`, "erro", superficie.intervalo, "Declare ao menos uma task associada, um impl explicito ou vinculos rastreaveis com codigo vivo."));
|
|
296
594
|
}
|
|
@@ -306,7 +604,13 @@ function validarSuperficie(superficie, tipoSuperficie, tasksConhecidas, tiposCon
|
|
|
306
604
|
if (effects) {
|
|
307
605
|
validarEfeitosDeclarados(effects.linhas, diagnosticos, `effects da superficie ${tipoSuperficie} ${nomeSuperficie}`);
|
|
308
606
|
}
|
|
607
|
+
validarExecucaoBloco(execucao, diagnosticos, `superficie ${tipoSuperficie} "${nomeSuperficie}"`);
|
|
309
608
|
validarVinculos(vinculos, diagnosticos, `${tipoSuperficie} ${nomeSuperficie}`);
|
|
609
|
+
const perfilSeguranca = coletarPerfilSegurancaDeclarado(superficie, effects, diagnosticos, `superficie ${tipoSuperficie} "${nomeSuperficie}"`);
|
|
610
|
+
emitirGuardrailsSeguranca(`Superficie ${tipoSuperficie} "${nomeSuperficie}"`, superficie.intervalo, perfilSeguranca, diagnosticos, {
|
|
611
|
+
publico: superficieEhPublica(superficie, tipoSuperficie),
|
|
612
|
+
sensivel: perfilSeguranca.efeitoPrivilegiado || perfilSeguranca.dadosSensiveis,
|
|
613
|
+
});
|
|
310
614
|
}
|
|
311
615
|
function recomporCaminhoRoute(campo) {
|
|
312
616
|
if (!campo) {
|
|
@@ -389,15 +693,23 @@ function validarEfeitosDeclarados(linhas, diagnosticos, contexto) {
|
|
|
389
693
|
for (const linha of linhas) {
|
|
390
694
|
const efeito = parsearEfeitoSemantico(linha.conteudo);
|
|
391
695
|
if (!efeito) {
|
|
392
|
-
diagnosticos.push(criarDiagnostico("SEM023", `Declaracao invalida de efeito em ${contexto}: "${linha.conteudo}".`, "erro", linha.intervalo, "Use o formato \"categoria alvo\" ou \"categoria alvo detalhe\",
|
|
696
|
+
diagnosticos.push(criarDiagnostico("SEM023", `Declaracao invalida de efeito em ${contexto}: "${linha.conteudo}".`, "erro", linha.intervalo, "Use o formato \"categoria alvo\" ou \"categoria alvo detalhe\", podendo adicionar criticidade=..., privilegio=... e isolamento=...."));
|
|
393
697
|
continue;
|
|
394
698
|
}
|
|
395
699
|
if (!ehCategoriaEfeitoSemantico(efeito.categoria)) {
|
|
396
|
-
diagnosticos.push(criarDiagnostico("SEM048", `Categoria de efeito "${efeito.categoria}" nao e suportada em ${contexto}.`, "erro", linha.intervalo, "Use
|
|
700
|
+
diagnosticos.push(criarDiagnostico("SEM048", `Categoria de efeito "${efeito.categoria}" nao e suportada em ${contexto}.`, "erro", linha.intervalo, "Use categorias como persistencia, consulta, evento, auditoria, db.write, queue.publish, fs.write, network.egress, secret.read ou shell.exec."));
|
|
397
701
|
}
|
|
398
702
|
if (efeito.criticidadeTexto && !ehCriticidadeEfeitoSemantico(efeito.criticidadeTexto)) {
|
|
399
703
|
diagnosticos.push(criarDiagnostico("SEM052", `Criticidade de efeito "${efeito.criticidadeTexto}" nao e suportada em ${contexto}.`, "erro", linha.intervalo, "Use apenas criticidade=baixa, criticidade=media, criticidade=alta ou criticidade=critica."));
|
|
400
704
|
}
|
|
705
|
+
if (efeito.privilegioTexto
|
|
706
|
+
&& !["leitura", "escrita", "publicacao", "execucao", "admin", "egress"].includes(efeito.privilegioTexto)) {
|
|
707
|
+
diagnosticos.push(criarDiagnostico("SEM092", `Privilegio de efeito "${efeito.privilegioTexto}" nao e suportado em ${contexto}.`, "erro", linha.intervalo, "Use privilegio=leitura, privilegio=escrita, privilegio=publicacao, privilegio=execucao, privilegio=admin ou privilegio=egress."));
|
|
708
|
+
}
|
|
709
|
+
if (efeito.isolamentoTexto
|
|
710
|
+
&& !["tenant", "processo", "host", "vps", "global"].includes(efeito.isolamentoTexto)) {
|
|
711
|
+
diagnosticos.push(criarDiagnostico("SEM093", `Isolamento de efeito "${efeito.isolamentoTexto}" nao e suportado em ${contexto}.`, "erro", linha.intervalo, "Use isolamento=tenant, isolamento=processo, isolamento=host, isolamento=vps ou isolamento=global."));
|
|
712
|
+
}
|
|
401
713
|
}
|
|
402
714
|
}
|
|
403
715
|
function validarState(state, tiposConhecidos, enumsConhecidos, diagnosticos) {
|
|
@@ -685,6 +997,11 @@ function validarRoute(route, tasksConhecidas, tarefasDetalhadas, diagnosticos) {
|
|
|
685
997
|
validarEfeitosDeclarados(effects.linhas, diagnosticos, `effects da route ${route.nome}`);
|
|
686
998
|
}
|
|
687
999
|
validarVinculos(route.vinculos, diagnosticos, `route ${route.nome}`);
|
|
1000
|
+
const perfilSeguranca = coletarPerfilSegurancaDeclarado(route.corpo, effects, diagnosticos, `route "${route.nome}"`);
|
|
1001
|
+
emitirGuardrailsSeguranca(`Route "${route.nome}"`, route.intervalo, perfilSeguranca, diagnosticos, {
|
|
1002
|
+
publico: extrairPerfilCompatibilidade(route.corpo, "publico") === "publico",
|
|
1003
|
+
sensivel: routeEhMutante(route) || perfilSeguranca.efeitoPrivilegiado || perfilSeguranca.dadosSensiveis,
|
|
1004
|
+
});
|
|
688
1005
|
if (task && !tasksConhecidas.has(task.valor)) {
|
|
689
1006
|
diagnosticos.push(criarDiagnostico("SEM018", `Route "${route.nome}" referencia task "${task.valor}" que nao existe.`, "erro", task.intervalo, "Ajuste o campo task da route para apontar para uma task declarada no modulo."));
|
|
690
1007
|
return;
|
|
@@ -693,6 +1010,82 @@ function validarRoute(route, tasksConhecidas, tarefasDetalhadas, diagnosticos) {
|
|
|
693
1010
|
validarContratoRoute(route, task.valor, tarefasDetalhadas, diagnosticos);
|
|
694
1011
|
}
|
|
695
1012
|
}
|
|
1013
|
+
function validarGuardrailsSeguranca(modulo, diagnosticos) {
|
|
1014
|
+
const superficies = coletarSuperficiesModulo(modulo);
|
|
1015
|
+
for (const task of modulo.tasks) {
|
|
1016
|
+
const motivos = new Set();
|
|
1017
|
+
const rotasPublicasAssociadas = modulo.routes.filter((route) => localizarCampo(route.corpo, "task", "tarefa")?.valor === task.nome
|
|
1018
|
+
&& extrairPerfilCompatibilidade(route.corpo, "publico") === "publico");
|
|
1019
|
+
const superficiesPublicasAssociadas = superficies.filter((item) => localizarCampo(item.superficie, "task", "tarefa")?.valor === task.nome
|
|
1020
|
+
&& superficieEhPublica(item.superficie, item.tipo));
|
|
1021
|
+
const perfilTask = coletarPerfilSegurancaDeclarado(task.corpo, task.effects, undefined, `task "${task.nome}"`);
|
|
1022
|
+
if (taskEhSensivel(task)) {
|
|
1023
|
+
motivos.add("criticidade operacional alta/critica ou efeito sensivel");
|
|
1024
|
+
}
|
|
1025
|
+
for (const route of rotasPublicasAssociadas) {
|
|
1026
|
+
motivos.add(`route publica "${route.nome}"`);
|
|
1027
|
+
}
|
|
1028
|
+
for (const item of superficiesPublicasAssociadas) {
|
|
1029
|
+
motivos.add(`superficie publica ${item.tipo} "${item.superficie.nome ?? item.tipo}"`);
|
|
1030
|
+
}
|
|
1031
|
+
const motivosOrdenados = [...motivos];
|
|
1032
|
+
if (motivosOrdenados.length === 0) {
|
|
1033
|
+
continue;
|
|
1034
|
+
}
|
|
1035
|
+
const perfisPublicos = [
|
|
1036
|
+
...rotasPublicasAssociadas.map((route) => coletarPerfilSegurancaDeclarado(route.corpo, localizarBloco(route.corpo, "effects"), undefined, `route "${route.nome}"`)),
|
|
1037
|
+
...superficiesPublicasAssociadas.map((item) => coletarPerfilSegurancaDeclarado(item.superficie, localizarBloco(item.superficie, "effects"), undefined, `superficie ${item.tipo} "${item.superficie.nome ?? item.tipo}"`)),
|
|
1038
|
+
];
|
|
1039
|
+
const perfilPublico = {
|
|
1040
|
+
auth: { ...perfilTask.auth, explicita: perfilTask.auth.explicita || perfisPublicos.some((perfil) => perfil.auth.explicita) },
|
|
1041
|
+
authz: {
|
|
1042
|
+
...perfilTask.authz,
|
|
1043
|
+
explicita: perfilTask.authz.explicita || perfisPublicos.some((perfil) => perfil.authz.explicita),
|
|
1044
|
+
papeis: [...new Set([perfilTask.authz.papeis, ...perfisPublicos.map((perfil) => perfil.authz.papeis)].flat())],
|
|
1045
|
+
escopos: [...new Set([perfilTask.authz.escopos, ...perfisPublicos.map((perfil) => perfil.authz.escopos)].flat())],
|
|
1046
|
+
},
|
|
1047
|
+
dados: {
|
|
1048
|
+
...perfilTask.dados,
|
|
1049
|
+
explicita: perfilTask.dados.explicita || perfisPublicos.some((perfil) => perfil.dados.explicita),
|
|
1050
|
+
campos: [...perfilTask.dados.campos, ...perfisPublicos.flatMap((perfil) => perfil.dados.campos)],
|
|
1051
|
+
},
|
|
1052
|
+
audit: { ...perfilTask.audit, explicita: perfilTask.audit.explicita || perfisPublicos.some((perfil) => perfil.audit.explicita) },
|
|
1053
|
+
segredos: {
|
|
1054
|
+
...perfilTask.segredos,
|
|
1055
|
+
explicita: perfilTask.segredos.explicita || perfisPublicos.some((perfil) => perfil.segredos.explicita),
|
|
1056
|
+
itens: [...perfilTask.segredos.itens, ...perfisPublicos.flatMap((perfil) => perfil.segredos.itens)],
|
|
1057
|
+
},
|
|
1058
|
+
forbidden: {
|
|
1059
|
+
...perfilTask.forbidden,
|
|
1060
|
+
explicita: perfilTask.forbidden.explicita || perfisPublicos.some((perfil) => perfil.forbidden.explicita),
|
|
1061
|
+
regras: [...new Set([perfilTask.forbidden.regras, ...perfisPublicos.map((perfil) => perfil.forbidden.regras)].flat())],
|
|
1062
|
+
},
|
|
1063
|
+
efeitoPrivilegiado: perfilTask.efeitoPrivilegiado || perfisPublicos.some((perfil) => perfil.efeitoPrivilegiado),
|
|
1064
|
+
dadosSensiveis: perfilTask.dadosSensiveis || perfisPublicos.some((perfil) => perfil.dadosSensiveis),
|
|
1065
|
+
exigeSegredos: perfilTask.exigeSegredos || perfisPublicos.some((perfil) => perfil.exigeSegredos),
|
|
1066
|
+
};
|
|
1067
|
+
if (rotasPublicasAssociadas.length > 0 || superficiesPublicasAssociadas.length > 0) {
|
|
1068
|
+
emitirGuardrailsSeguranca(`Task "${task.nome}" exposta publicamente`, task.intervalo, perfilPublico, diagnosticos, { publico: true, sensivel: false });
|
|
1069
|
+
}
|
|
1070
|
+
const resumoMotivos = motivosOrdenados.join(", ");
|
|
1071
|
+
if (!task.execucao) {
|
|
1072
|
+
diagnosticos.push(criarDiagnostico("SEM071", `Task "${task.nome}" exige execucao explicita para producao por causa de ${resumoMotivos}, mas ainda opera com execucao implicita.`, "aviso", task.intervalo, "Declare timeout, retry, compensacao, idempotencia e criticidade_operacional no bloco execucao da task."));
|
|
1073
|
+
}
|
|
1074
|
+
if (!taskTemRastreabilidade(task)) {
|
|
1075
|
+
diagnosticos.push(criarDiagnostico("SEM072", `Task "${task.nome}" exige rastreabilidade forte por causa de ${resumoMotivos}, mas ainda nao declara impl nem vinculos.`, "aviso", task.intervalo, "Adicione impl e/ou vinculos para apontar arquivo, simbolo, recurso ou superficie real do codigo vivo."));
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
for (const item of superficies) {
|
|
1079
|
+
if (!superficieEhPublica(item.superficie, item.tipo)) {
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
const execucao = localizarBloco(item.superficie, "execucao");
|
|
1083
|
+
if (!execucao) {
|
|
1084
|
+
const nomeSuperficie = item.superficie.nome ?? item.tipo;
|
|
1085
|
+
diagnosticos.push(criarDiagnostico("SEM073", `Superficie publica ${item.tipo} "${nomeSuperficie}" deveria declarar execucao explicita para producao, mas ainda depende do padrao implicito.`, "aviso", item.superficie.intervalo, `Declare timeout, retry, compensacao e criticidade_operacional no proprio bloco ${item.tipo}.`));
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
696
1089
|
export function criarContextoLocal(modulo) {
|
|
697
1090
|
const simbolos = new Map();
|
|
698
1091
|
const tiposConhecidos = new Set(TIPOS_PRIMITIVOS);
|
|
@@ -831,6 +1224,11 @@ function validarTask(task, tiposConhecidos, statesConhecidos, diagnosticos) {
|
|
|
831
1224
|
}
|
|
832
1225
|
validarVinculos(task.vinculos, diagnosticos, `task ${task.nome}`);
|
|
833
1226
|
validarExecucao(task, diagnosticos);
|
|
1227
|
+
const perfilSeguranca = coletarPerfilSegurancaDeclarado(task.corpo, task.effects, diagnosticos, `task "${task.nome}"`);
|
|
1228
|
+
emitirGuardrailsSeguranca(`Task "${task.nome}"`, task.intervalo, perfilSeguranca, diagnosticos, {
|
|
1229
|
+
publico: false,
|
|
1230
|
+
sensivel: taskEhSensivel(task) || perfilSeguranca.efeitoPrivilegiado || perfilSeguranca.dadosSensiveis || perfilSeguranca.exigeSegredos,
|
|
1231
|
+
});
|
|
834
1232
|
validarImplementacoesTask(task, diagnosticos);
|
|
835
1233
|
if (task.tests) {
|
|
836
1234
|
for (const bloco of task.tests.blocos) {
|
|
@@ -1062,6 +1460,7 @@ export function analisarSemantica(modulo, opcoes = {}) {
|
|
|
1062
1460
|
for (const policy of modulo.policies) {
|
|
1063
1461
|
validarSuperficie(policy, "policy", tasksConhecidas, tiposConhecidos, diagnosticos);
|
|
1064
1462
|
}
|
|
1463
|
+
validarGuardrailsSeguranca(modulo, diagnosticos);
|
|
1065
1464
|
const assinaturasRoute = new Map();
|
|
1066
1465
|
for (const route of modulo.routes) {
|
|
1067
1466
|
const metodo = (localizarCampo(route.corpo, "metodo")?.valor ?? "").toUpperCase();
|