@pathbit/agentdevkit 0.0.9 → 0.0.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/.pbai4devs/COMPATIBILIDADE.md +6 -0
- package/.pbai4devs/INSTRUCTIONS.md +3 -0
- package/.pbai4devs/agents/README.md +19 -0
- package/.pbai4devs/agents/auditor-seguranca/AGENT.md +20 -0
- package/.pbai4devs/agents/auditor-seguranca/antigravity.yaml +8 -0
- package/.pbai4devs/agents/auditor-seguranca/claude.yaml +5 -0
- package/.pbai4devs/agents/auditor-seguranca/cursor.yaml +5 -0
- package/.pbai4devs/agents/auditor-seguranca/gemini.yaml +6 -0
- package/.pbai4devs/agents/auditor-seguranca/github.yaml +5 -0
- package/.pbai4devs/agents/auditor-seguranca/kimi-code.yaml +5 -0
- package/.pbai4devs/agents/auditor-seguranca/mimocode.yaml +8 -0
- package/.pbai4devs/agents/auditor-seguranca/opencode.yaml +11 -0
- package/.pbai4devs/agents/documentador/AGENT.md +19 -0
- package/.pbai4devs/agents/documentador/antigravity.yaml +8 -0
- package/.pbai4devs/agents/documentador/claude.yaml +5 -0
- package/.pbai4devs/agents/documentador/cursor.yaml +5 -0
- package/.pbai4devs/agents/documentador/gemini.yaml +6 -0
- package/.pbai4devs/agents/documentador/github.yaml +5 -0
- package/.pbai4devs/agents/documentador/kimi-code.yaml +5 -0
- package/.pbai4devs/agents/documentador/mimocode.yaml +8 -0
- package/.pbai4devs/agents/documentador/opencode.yaml +11 -0
- package/.pbai4devs/agents/implementador/AGENT.md +20 -0
- package/.pbai4devs/agents/implementador/antigravity.yaml +8 -0
- package/.pbai4devs/agents/implementador/claude.yaml +5 -0
- package/.pbai4devs/agents/implementador/cursor.yaml +5 -0
- package/.pbai4devs/agents/implementador/gemini.yaml +6 -0
- package/.pbai4devs/agents/implementador/github.yaml +5 -0
- package/.pbai4devs/agents/implementador/kimi-code.yaml +5 -0
- package/.pbai4devs/agents/implementador/mimocode.yaml +8 -0
- package/.pbai4devs/agents/implementador/opencode.yaml +11 -0
- package/.pbai4devs/agents/planejador/AGENT.md +19 -0
- package/.pbai4devs/agents/planejador/antigravity.yaml +8 -0
- package/.pbai4devs/agents/planejador/claude.yaml +5 -0
- package/.pbai4devs/agents/planejador/cursor.yaml +5 -0
- package/.pbai4devs/agents/planejador/gemini.yaml +6 -0
- package/.pbai4devs/agents/planejador/github.yaml +5 -0
- package/.pbai4devs/agents/planejador/kimi-code.yaml +5 -0
- package/.pbai4devs/agents/planejador/mimocode.yaml +8 -0
- package/.pbai4devs/agents/planejador/opencode.yaml +11 -0
- package/.pbai4devs/agents/validador/AGENT.md +19 -0
- package/.pbai4devs/agents/validador/antigravity.yaml +8 -0
- package/.pbai4devs/agents/validador/claude.yaml +5 -0
- package/.pbai4devs/agents/validador/cursor.yaml +5 -0
- package/.pbai4devs/agents/validador/gemini.yaml +6 -0
- package/.pbai4devs/agents/validador/github.yaml +5 -0
- package/.pbai4devs/agents/validador/kimi-code.yaml +5 -0
- package/.pbai4devs/agents/validador/mimocode.yaml +8 -0
- package/.pbai4devs/agents/validador/opencode.yaml +11 -0
- package/.pbai4devs/commands/README.md +13 -0
- package/.pbai4devs/commands/atualizar-documentacao/COMMAND.md +17 -0
- package/.pbai4devs/commands/auditar-seguranca/COMMAND.md +25 -0
- package/.pbai4devs/commands/implementar-entrega/COMMAND.md +20 -0
- package/.pbai4devs/commands/planejar-entrega/COMMAND.md +17 -0
- package/.pbai4devs/commands/preparar-seguranca/COMMAND.md +22 -0
- package/.pbai4devs/commands/validar-entrega/COMMAND.md +18 -0
- package/.pbai4devs/commands/verificar-dependencias/COMMAND.md +18 -0
- package/.pbai4devs/commands/verificar-segredos/COMMAND.md +19 -0
- package/.pbai4devs/external-skills/CATALOG.json +39 -1
- package/.pbai4devs/external-skills/README.md +13 -0
- package/.pbai4devs/rules/README.md +4 -0
- package/.pbai4devs/rules/execucao-local/RULE.md +20 -0
- package/.pbai4devs/rules/execucao-local/cursor.yaml +4 -0
- package/.pbai4devs/rules/seguranca/RULE.md +19 -0
- package/.pbai4devs/rules/seguranca/cursor.yaml +4 -0
- package/.pbai4devs/scripts/README.md +1 -0
- package/.pbai4devs/scripts/auditar-seguranca/README.md +57 -0
- package/.pbai4devs/scripts/auditar-seguranca/SCRIPT.mjs +411 -0
- package/.pbai4devs/scripts/auditar-seguranca/database-zip.mjs +143 -0
- package/.pbai4devs/scripts/auditar-seguranca/rules/javascript.yml +50 -0
- package/.pbai4devs/scripts/auditar-seguranca/tests/SMOKE.mjs +74 -0
- package/.pbai4devs/scripts/auditar-seguranca/tests/TEST.mjs +247 -0
- package/.pbai4devs/scripts/auditar-seguranca/tests/zip-fixture.mjs +45 -0
- package/.pbai4devs/scripts/lib/native-release.mjs +193 -0
- package/.pbai4devs/scripts/preparar-ferramentas/README.md +8 -2
- package/.pbai4devs/scripts/preparar-ferramentas/SCRIPT.mjs +46 -16
- package/.pbai4devs/scripts/preparar-ferramentas/tests/TEST.mjs +2 -0
- package/.pbai4devs/scripts/preparar-ferramentas/tests/native-release.test.mjs +103 -0
- package/.pbai4devs/skills/README.md +10 -2
- package/.pbai4devs/skills/projeto/preparar-ferramentas/SKILL.md +5 -1
- package/.pbai4devs/skills/seguranca/REFERENCE.md +104 -0
- package/.pbai4devs/skills/seguranca/corrigir-vulnerabilidade/SKILL.md +27 -0
- package/.pbai4devs/skills/seguranca/modelar-ameacas/SKILL.md +25 -0
- package/.pbai4devs/skills/seguranca/revisar-autorizacao/SKILL.md +29 -0
- package/.pbai4devs/skills/seguranca/revisar-seguranca/SKILL.md +27 -0
- package/.pbai4devs/templates/README.md +2 -0
- package/.pbai4devs/templates/revisao-seguranca/TEMPLATE.md +33 -0
- package/.pbai4devs/tools/README.md +11 -3
- package/.pbai4devs/tools/gitleaks/README.md +22 -0
- package/.pbai4devs/tools/gitleaks/SCRIPT.mjs +5 -0
- package/.pbai4devs/tools/gitleaks/TOOL.json +67 -0
- package/.pbai4devs/tools/opengrep/README.md +22 -0
- package/.pbai4devs/tools/opengrep/SCRIPT.mjs +5 -0
- package/.pbai4devs/tools/opengrep/TOOL.json +71 -0
- package/.pbai4devs/tools/osv-scanner/README.md +23 -0
- package/.pbai4devs/tools/osv-scanner/SCRIPT.mjs +5 -0
- package/.pbai4devs/tools/osv-scanner/TOOL.json +70 -0
- package/CHANGELOG.md +17 -0
- package/README.en.md +18 -5
- package/README.es.md +19 -6
- package/README.md +16 -3
- package/docs/README.md +9 -5
- package/docs/guides/README.md +3 -1
- package/docs/guides/cli.md +3 -0
- package/docs/guides/installation.md +61 -11
- package/docs/guides/prerequisites.md +123 -0
- package/docs/guides/resources.md +48 -1
- package/docs/guides/security-references.md +97 -0
- package/docs/guides/security.md +32 -0
- package/docs/guides/tools-and-testing.md +2 -1
- package/docs/guides/troubleshooting.md +23 -7
- package/docs/maintainers/README.md +1 -1
- package/docs/maintainers/maintenance.md +15 -4
- package/docs/maintainers/packages.md +45 -13
- package/docs/product/prd.md +4 -1
- package/docs/product/roadmap.md +4 -0
- package/docs/specs/README.md +3 -0
- package/docs/specs/dual-registry/SPEC.md +43 -0
- package/docs/specs/security-ecosystem/README.md +9 -0
- package/docs/specs/security-ecosystem/SPEC.md +53 -0
- package/docs/specs/v0.0.10/README.md +9 -0
- package/docs/specs/v0.0.10/SPEC.md +42 -0
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
6
6
|
import { randomUUID } from 'node:crypto';
|
|
7
7
|
import { hostname } from 'node:os';
|
|
8
8
|
import { projectRoot, requireNode, isMain } from '../lib/runtime.mjs';
|
|
9
|
+
import { validateNativeDownload, nativeRelease, validateNativeRelease, installNative, checkNative, assertNativeVersion } from '../lib/native-release.mjs';
|
|
9
10
|
|
|
10
11
|
const registry = 'https://registry.npmjs.org/';
|
|
11
12
|
const versionPattern = /^\d+\.\d+\.\d+(?:-[\w.-]+)?$/;
|
|
@@ -29,9 +30,13 @@ export function validateToolConfig(name, value) {
|
|
|
29
30
|
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name) || value?.name !== name) fail(`Nome de ferramenta invalido: ${name}`);
|
|
30
31
|
if (typeof value.description !== 'string' || !value.description.trim() || value.description.includes('\0')) fail(`Descricao ausente ou invalida: ${name}`);
|
|
31
32
|
const download = value.download;
|
|
32
|
-
if (
|
|
33
|
-
if (
|
|
33
|
+
if (!['npm', 'github-release'].includes(download?.provider)) fail(`Download nao suportado: ${name}`);
|
|
34
|
+
if (download.provider === 'npm') {
|
|
35
|
+
if (download.registry !== registry || download.channel !== 'latest') fail(`Download nao suportado: ${name}`);
|
|
36
|
+
if (!/^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/.test(download.package || '') || !/^[\w.-]+\/[\w.-]+$/.test(download.repository || '')) fail(`Origem invalida: ${name}`);
|
|
37
|
+
}
|
|
34
38
|
if (!/^[a-z][a-z0-9-]*$/.test(value.group || '') || !Array.isArray(value.platforms) || !value.platforms.length || new Set(value.platforms).size !== value.platforms.length || value.platforms.some(item => !/^(?:darwin|linux|win32)-(?:arm64|x64)$/.test(item))) fail(`Grupo/plataformas invalidos: ${name}`);
|
|
39
|
+
if (download.provider === 'github-release') validateNativeDownload(download, value.platforms);
|
|
35
40
|
if (!Array.isArray(value.probe) || !value.probe.length || value.probe.some(item => typeof item !== 'string' || item.includes('\0')) || (value.probeInput !== undefined && (typeof value.probeInput !== 'string' || value.probeInput.includes('\0')))) fail(`Verificacao invalida: ${name}`);
|
|
36
41
|
if (value.binary !== undefined && (typeof value.binary !== 'string' || !/^[\w.-]+$/.test(value.binary))) fail(`Executavel invalido: ${name}`);
|
|
37
42
|
if (value.executables !== undefined) {
|
|
@@ -72,7 +77,7 @@ export function toolActions(name, entry) {
|
|
|
72
77
|
validateToolConfig(name, entry);
|
|
73
78
|
const descriptions = {
|
|
74
79
|
install: 'Instala ou reutiliza uma instalacao saudavel.',
|
|
75
|
-
update: 'Consulta latest e atualiza com rollback em caso de falha.',
|
|
80
|
+
update: entry.download.provider === 'github-release' ? 'Aplica a versao revisada do catalogo com rollback.' : 'Consulta latest e atualiza com rollback em caso de falha.',
|
|
76
81
|
check: 'Executa o probe local sem instalar nem consultar latest.',
|
|
77
82
|
info: 'Mostra configuracao, plataforma, runtime e versao registrada.',
|
|
78
83
|
run: 'Executa o CLI instalado com os argumentos informados.',
|
|
@@ -128,7 +133,8 @@ export function loadCatalog(root = projectRoot) {
|
|
|
128
133
|
return catalog;
|
|
129
134
|
}
|
|
130
135
|
|
|
131
|
-
export function validateRelease(name, entry, release) {
|
|
136
|
+
export function validateRelease(name, entry, release, platform = platformKey()) {
|
|
137
|
+
if (entry.download.provider === 'github-release') return validateNativeRelease(entry, release, platform);
|
|
132
138
|
const expected = entry.download.package;
|
|
133
139
|
const repository = typeof release.repository === 'string' ? release.repository : release.repository?.url;
|
|
134
140
|
if (release.name !== expected || !versionPattern.test(release.version || '')) fail(`Nome/versao invalido no registro: ${name}`);
|
|
@@ -165,6 +171,9 @@ export function binaryInvocation(prefix, name, args = [], binaryName = name.spli
|
|
|
165
171
|
|
|
166
172
|
export function toolEnvironment(entry, local, runtime) {
|
|
167
173
|
const environment = Object.assign(Object.create(null), process.env);
|
|
174
|
+
if (entry.download?.provider === 'github-release') {
|
|
175
|
+
for (const key of Object.keys(environment)) if (/token|secret|password|(?:^|_)auth(?:_|$)|^(?:semgrep|opengrep)_/i.test(key)) delete environment[key];
|
|
176
|
+
}
|
|
168
177
|
const set = (key, value) => {
|
|
169
178
|
if (process.platform === 'win32') for (const existing of Object.keys(environment)) if (existing.toUpperCase() === key.toUpperCase()) delete environment[existing];
|
|
170
179
|
environment[key] = value;
|
|
@@ -184,10 +193,13 @@ export function toolEnvironment(entry, local, runtime) {
|
|
|
184
193
|
}
|
|
185
194
|
|
|
186
195
|
function execute(prefix, entry, args, capture = false, input, environment, runtime) {
|
|
187
|
-
const
|
|
196
|
+
const native = entry.download.provider === 'github-release';
|
|
197
|
+
const [file, options] = native ? [checkNative(prefix, entry, runtime.platform || platformKey()).file, args]
|
|
198
|
+
: binaryInvocation(prefix, entry.download.package, args, entry.binary || entry.name, runtime.file);
|
|
188
199
|
const result = spawnSync(file, options, { stdio: capture ? 'pipe' : 'inherit', cwd: capture ? prefix : undefined, input, env: environment, encoding: 'utf8', timeout: capture ? 30000 : undefined, windowsHide: true });
|
|
189
200
|
if (result.error) throw result.error;
|
|
190
201
|
if (capture && result.status !== 0) fail(`${entry.name}: ${result.stderr?.trim().slice(0, 400) || result.stdout?.trim().slice(0, 400) || `falha ${result.status}`}`, result.status ?? 1);
|
|
202
|
+
if (capture && native) assertNativeVersion(`${result.stdout || ''}\n${result.stderr || ''}`, entry.download.version);
|
|
191
203
|
return result.status ?? 1;
|
|
192
204
|
}
|
|
193
205
|
|
|
@@ -215,11 +227,13 @@ export class ToolManager {
|
|
|
215
227
|
this.catalog = catalog ?? loadCatalog(this.root);
|
|
216
228
|
for (const [name, entry] of Object.entries(this.catalog)) validateToolConfig(name, entry);
|
|
217
229
|
this.fetchRelease = fetchRelease || (async (name, entry) => {
|
|
230
|
+
if (entry.download.provider === 'github-release') return nativeRelease(entry, this.target);
|
|
218
231
|
const response = await fetch(`${entry.download.registry}${encodeURIComponent(entry.download.package)}/${entry.download.channel}`, { signal: AbortSignal.timeout(20000) });
|
|
219
232
|
if (!response.ok) fail(`Registro indisponivel: HTTP ${response.status}`);
|
|
220
233
|
return response.json();
|
|
221
234
|
});
|
|
222
|
-
this.installPackage = install || ((prefix, packageName, version, entry, runtime) => {
|
|
235
|
+
this.installPackage = install || (async (prefix, packageName, version, entry, runtime) => {
|
|
236
|
+
if (entry.download.provider === 'github-release') return installNative(prefix, entry, this.target);
|
|
223
237
|
const environment = toolEnvironment(entry, this.local(entry.name), runtime);
|
|
224
238
|
const result = spawnSync(runtime.file, [this.npm(runtime, environment), 'install', '--prefix', prefix, '--cache', join(this.home, '.cache/npm'), '--registry', entry.download.registry, '--ignore-scripts', '--omit=dev', '--no-audit', '--no-fund', '--save-exact', `${packageName}@${version}`], {
|
|
225
239
|
cwd: prefix, encoding: 'utf8', timeout: 180000,
|
|
@@ -236,7 +250,7 @@ export class ToolManager {
|
|
|
236
250
|
const entry = this.entry(name);
|
|
237
251
|
if (!entry.platforms.includes(this.target)) fail(`Plataforma nao suportada por ${name}: ${this.target}`);
|
|
238
252
|
requireNode(24, this.nodeVersion);
|
|
239
|
-
return { file: process.execPath, version: process.version };
|
|
253
|
+
return { file: process.execPath, version: process.version, platform: this.target };
|
|
240
254
|
}
|
|
241
255
|
preflight(names) {
|
|
242
256
|
const issues = [];
|
|
@@ -298,8 +312,13 @@ export class ToolManager {
|
|
|
298
312
|
}
|
|
299
313
|
check(name) {
|
|
300
314
|
const entry = this.entry(name); const runtime = this.runtime(name); const info = this.current(name);
|
|
301
|
-
|
|
302
|
-
|
|
315
|
+
if (entry.download.provider === 'github-release') {
|
|
316
|
+
const receipt = checkNative(info.prefix, entry, this.target);
|
|
317
|
+
if (receipt.version !== info.version || receipt.binarySha256 !== info.binarySha256) fail(`Versao local divergente: ${name}`);
|
|
318
|
+
} else {
|
|
319
|
+
const pkg = json(join(info.prefix, 'node_modules', entry.download.package, 'package.json'));
|
|
320
|
+
if (pkg.name !== entry.download.package || pkg.version !== info.version) fail(`Versao local divergente: ${name}`);
|
|
321
|
+
}
|
|
303
322
|
execute(info.prefix, entry, entry.probe, true, entry.probeInput, toolEnvironment(entry, this.local(name), runtime), runtime);
|
|
304
323
|
return info;
|
|
305
324
|
}
|
|
@@ -399,7 +418,7 @@ export class ToolManager {
|
|
|
399
418
|
renameSync(storagePath(this.root, temporary, 'file'), storagePath(this.root, join(local, 'CURRENT.json'), 'file'));
|
|
400
419
|
}
|
|
401
420
|
async updateLocked(name) {
|
|
402
|
-
const entry = this.entry(name); const runtime = this.runtime(name); const release = validateRelease(name, entry, await this.fetchRelease(name, entry));
|
|
421
|
+
const entry = this.entry(name); const runtime = this.runtime(name); const release = validateRelease(name, entry, await this.fetchRelease(name, entry), this.target);
|
|
403
422
|
const { local } = this.storage(name);
|
|
404
423
|
const target = storagePath(this.root, join(local, 'versions', `${release.version}-${this.target}`), 'directory');
|
|
405
424
|
let installed;
|
|
@@ -409,14 +428,21 @@ export class ToolManager {
|
|
|
409
428
|
const stage = mkdtempSync(join(local, '.stage-'));
|
|
410
429
|
const temporary = storagePath(this.root, join(local, `CURRENT.${randomUUID()}.json`), 'file'); let previous; let moved = false;
|
|
411
430
|
try {
|
|
412
|
-
|
|
431
|
+
if (entry.download.provider === 'github-release') {
|
|
432
|
+
for (const location of Object.values(entry.envPaths || {})) mkdirSync(storagePath(this.root, join(local, location), 'directory'), { recursive: true });
|
|
433
|
+
}
|
|
434
|
+
await this.installPackage(stage, entry.download.package, release.version, entry, runtime);
|
|
413
435
|
this.storage(name); storagePath(this.root, stage, 'directory');
|
|
414
|
-
|
|
415
|
-
if (
|
|
416
|
-
|
|
436
|
+
let receipt;
|
|
437
|
+
if (entry.download.provider === 'github-release') receipt = checkNative(stage, entry, this.target);
|
|
438
|
+
else {
|
|
439
|
+
const pkg = json(join(stage, 'node_modules', entry.download.package, 'package.json')); const lock = json(join(stage, 'package-lock.json'));
|
|
440
|
+
if (pkg.name !== entry.download.package || pkg.version !== release.version || lock.packages?.[`node_modules/${entry.download.package}`]?.integrity !== release.dist.integrity) fail('Pacote instalado nao corresponde ao registro.');
|
|
441
|
+
prepareExecutables(stage, entry, this.target);
|
|
442
|
+
}
|
|
417
443
|
execute(stage, entry, entry.probe, true, entry.probeInput, toolEnvironment(entry, local, runtime), runtime);
|
|
418
444
|
this.storage(name); storagePath(this.root, target, 'directory');
|
|
419
|
-
const info = { name, package: entry.download.package, version: release.version, platform: this.target, node: runtime.version, nodePath: runtime.file, source: entry.source, tarball: release.dist.tarball, integrity: release.dist.integrity, checkedAt: new Date().toISOString() };
|
|
445
|
+
const info = { name, ...(receipt ? { provider: 'github-release', binarySha256: receipt.binarySha256, archiveSha256: receipt.archiveSha256 } : { package: entry.download.package }), version: release.version, platform: this.target, node: runtime.version, nodePath: runtime.file, source: entry.source, tarball: receipt ? release.url : release.dist.tarball, integrity: receipt ? `sha256-${Buffer.from(receipt.archiveSha256, 'hex').toString('base64')}` : release.dist.integrity, checkedAt: new Date().toISOString() };
|
|
420
446
|
writeFileSync(storagePath(this.root, temporary, 'file'), `${JSON.stringify(info, null, 2)}\n`, { flag: 'wx' });
|
|
421
447
|
this.launchers(name);
|
|
422
448
|
if (existsSync(target)) { previous = storagePath(this.root, `${target}.previous-${randomUUID()}`, 'directory'); renameSync(storagePath(this.root, target, 'directory'), previous); }
|
|
@@ -425,7 +451,11 @@ export class ToolManager {
|
|
|
425
451
|
return { ...info, layout: 'local', prefix: target, changed: true };
|
|
426
452
|
} finally { rmSync(storagePath(this.root, stage, 'directory'), { recursive: true, force: true }); rmSync(storagePath(this.root, temporary, 'file'), { force: true }); }
|
|
427
453
|
}
|
|
428
|
-
run(name, args) {
|
|
454
|
+
run(name, args) {
|
|
455
|
+
const entry = this.entry(name), runtime = this.runtime(name), current = this.current(name);
|
|
456
|
+
if (entry.download.provider === 'github-release' && checkNative(current.prefix, entry, this.target).binarySha256 !== current.binarySha256) fail(`Recibo local divergente: ${name}`);
|
|
457
|
+
return execute(current.prefix, entry, args, false, undefined, toolEnvironment(entry, this.local(name), runtime), runtime);
|
|
458
|
+
}
|
|
429
459
|
}
|
|
430
460
|
|
|
431
461
|
const help = 'Uso: setup [all|grupo|nome]; [install|update|check|info] [base|all|grupo|nome]; list; unlock <nome>; run <nome> [argumentos]; --tool <nome> <acao> [argumentos].';
|
|
@@ -8,6 +8,7 @@ import { hostname } from 'node:os';
|
|
|
8
8
|
import { ToolManager, platformKey, validateRelease, validateToolConfig, loadCatalog, main, toolEnvironment, binaryInvocation, toolActions, toolWrapper } from '../SCRIPT.mjs';
|
|
9
9
|
import { requireNode } from '../../lib/runtime.mjs';
|
|
10
10
|
import './bootstrap.test.mjs';
|
|
11
|
+
import './native-release.test.mjs';
|
|
11
12
|
|
|
12
13
|
const project = resolve(dirname(fileURLToPath(import.meta.url)), '../../../..');
|
|
13
14
|
const tool = (name = 'test-tool', group = 'base') => ({ name, description: 'Ferramenta da fixture.', group, source: `https://github.com/owner/${name}`, download: { provider: 'npm', package: name, registry: 'https://registry.npmjs.org/', repository: `owner/${name}`, channel: 'latest' }, platforms: [platformKey()], scripts: { entry: 'SCRIPT.mjs' }, probe: ['--version'] });
|
|
@@ -44,6 +45,7 @@ function fixture(t, catalog = { 'test-tool': tool() }, options = {}) {
|
|
|
44
45
|
copyFileSync(join(project, '.pbai4devs/scripts/preparar-ferramentas/SCRIPT.mjs'), join(common, 'SCRIPT.mjs'));
|
|
45
46
|
mkdirSync(join(root, '.pbai4devs/scripts/lib'), { recursive: true });
|
|
46
47
|
copyFileSync(join(project, '.pbai4devs/scripts/lib/runtime.mjs'), join(root, '.pbai4devs/scripts/lib/runtime.mjs'));
|
|
48
|
+
copyFileSync(join(project, '.pbai4devs/scripts/lib/native-release.mjs'), join(root, '.pbai4devs/scripts/lib/native-release.mjs'));
|
|
47
49
|
const state = { version: '1.0.0', installs: 0, fetches: 0, fail: false, tamper: false, broken: false, noBin: false };
|
|
48
50
|
const manager = new ToolManager({ root, catalog, fetchRelease: async name => { state.fetches++; return release(state.version, name); }, install(prefix, name, version) { state.installs++; if (state.fail) throw new Error('download de prova falhou'); packageFiles(prefix, name, version, state); }, ...options });
|
|
49
51
|
return { root, manager, state, local: join(root, '.pbai4devs/tools/test-tool/.local') };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { gzipSync, deflateRawSync } from 'node:zlib';
|
|
4
|
+
import { mkdtempSync, mkdirSync, readFileSync, writeFileSync, rmSync, symlinkSync, existsSync } from 'node:fs';
|
|
5
|
+
import { dirname, join, resolve } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { nativeHash, nativeRelease, validateNativeDownload, validateNativeRelease, downloadNative, extractNative, installNative, checkNative, assertNativeVersion } from '../../lib/native-release.mjs';
|
|
8
|
+
import { ToolManager, platformKey, toolEnvironment } from '../SCRIPT.mjs';
|
|
9
|
+
|
|
10
|
+
const project = resolve(dirname(fileURLToPath(import.meta.url)), '../../../..');
|
|
11
|
+
function fixture(t) { const dir = join(project, '.agent-tests'); mkdirSync(dir, { recursive: true }); const root = mkdtempSync(join(dir, 'native tools ')); t.after(() => rmSync(root, { recursive: true, force: true })); return root; }
|
|
12
|
+
function config(bytes = Buffer.from('#!/bin/sh\necho 1.2.3\n'), version = '1.2.3') {
|
|
13
|
+
return { name: 'native-test', description: 'Native fixture.', group: 'security', source: 'https://github.com/owner/repo',
|
|
14
|
+
platforms: [platformKey()], scripts: { entry: 'SCRIPT.mjs' }, probe: ['--version'],
|
|
15
|
+
download: { provider: 'github-release', repository: 'owner/repo', channel: 'reviewed', version, tag: `v${version}`,
|
|
16
|
+
assets: { [platformKey()]: { file: 'tool.bin', format: 'raw', binary: 'tool', sha256: nativeHash(bytes) } } } };
|
|
17
|
+
}
|
|
18
|
+
function tar(entries) {
|
|
19
|
+
const chunks = [];
|
|
20
|
+
for (const { name, bytes = Buffer.from('binary'), type = '0' } of entries) {
|
|
21
|
+
const header = Buffer.alloc(512); header.write(name, 0, 100); header.write('0000755\0', 100); header.write('0000000\0', 108); header.write('0000000\0', 116);
|
|
22
|
+
header.write(bytes.length.toString(8).padStart(11, '0') + '\0', 124); header.write('00000000000\0', 136); header.fill(32, 148, 156); header.write(type, 156); header.write('ustar\0', 257);
|
|
23
|
+
header.write([...header].reduce((a, b) => a + b, 0).toString(8).padStart(6, '0') + '\0 ', 148);
|
|
24
|
+
chunks.push(header, bytes, Buffer.alloc((512 - bytes.length % 512) % 512));
|
|
25
|
+
}
|
|
26
|
+
return gzipSync(Buffer.concat([...chunks, Buffer.alloc(1024)]));
|
|
27
|
+
}
|
|
28
|
+
function crc32(bytes) { let crc = 0xffffffff; for (const b of bytes) { crc ^= b; for (let i = 0; i < 8; i++) crc = (crc >>> 1) ^ (0xedb88320 & -(crc & 1)); } return (crc ^ 0xffffffff) >>> 0; }
|
|
29
|
+
function zip(entries) {
|
|
30
|
+
const locals = [], centrals = []; let cursor = 0;
|
|
31
|
+
for (const { name, bytes = Buffer.from('binary'), mode = 0o100755, method = 8 } of entries) {
|
|
32
|
+
const filename = Buffer.from(name), compressed = method === 8 ? deflateRawSync(bytes) : bytes, crc = crc32(bytes);
|
|
33
|
+
const local = Buffer.alloc(30); local.writeUInt32LE(0x04034b50); local.writeUInt16LE(20, 4); local.writeUInt16LE(method, 8); local.writeUInt32LE(crc, 14); local.writeUInt32LE(compressed.length, 18); local.writeUInt32LE(bytes.length, 22); local.writeUInt16LE(filename.length, 26);
|
|
34
|
+
const central = Buffer.alloc(46); central.writeUInt32LE(0x02014b50); central.writeUInt16LE(3 << 8 | 20, 4); central.writeUInt16LE(20, 6); central.writeUInt16LE(method, 10); central.writeUInt32LE(crc, 16); central.writeUInt32LE(compressed.length, 20); central.writeUInt32LE(bytes.length, 24); central.writeUInt16LE(filename.length, 28); central.writeUInt32LE((mode << 16) >>> 0, 38); central.writeUInt32LE(cursor, 42);
|
|
35
|
+
locals.push(local, filename, compressed); centrals.push(central, filename); cursor += local.length + filename.length + compressed.length;
|
|
36
|
+
}
|
|
37
|
+
const central = Buffer.concat(centrals), end = Buffer.alloc(22); end.writeUInt32LE(0x06054b50); end.writeUInt16LE(entries.length, 8); end.writeUInt16LE(entries.length, 10); end.writeUInt32LE(central.length, 12); end.writeUInt32LE(cursor, 16);
|
|
38
|
+
return Buffer.concat([...locals, central, end]);
|
|
39
|
+
}
|
|
40
|
+
const asset = (bytes, format, binary = 'tool') => ({ format, binary, sha256: nativeHash(bytes) });
|
|
41
|
+
|
|
42
|
+
test('native release requires reviewed pins and exact platform assets', () => {
|
|
43
|
+
const entry = config(); assert.equal(validateNativeDownload(entry.download, entry.platforms), entry.download);
|
|
44
|
+
const release = nativeRelease(entry, platformKey()); assert.equal(validateNativeRelease(entry, release, platformKey()), release);
|
|
45
|
+
for (const change of [{ channel: 'latest' }, { tag: 'master' }, { version: '1.2.3-rc.1' }, { repository: '../evil' }, { assets: {} }]) assert.throws(() => validateNativeDownload({ ...entry.download, ...change }, entry.platforms));
|
|
46
|
+
assert.throws(() => validateNativeRelease(entry, { ...release, url: 'https://evil.example/tool' }, platformKey()));
|
|
47
|
+
const invalid = config(); invalid.download.assets[platformKey()].binary = '../escape'; assert.throws(() => validateNativeDownload(invalid.download, invalid.platforms));
|
|
48
|
+
assert.throws(() => assertNativeVersion('version 1.2.30', '1.2.3')); assert.throws(() => assertNativeVersion('version 1.2.3-rc.1', '1.2.3')); assertNativeVersion('Tool v1.2.3\n', '1.2.3');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('native download follows only anonymous official asset hosts with limits', async () => {
|
|
52
|
+
const seen = [];
|
|
53
|
+
const fetcher = async (url, options) => { seen.push({ url, options }); return seen.length === 1 ? new Response(null, { status: 302, headers: { location: 'https://release-assets.githubusercontent.com/file?signature=x' } }) : new Response('data'); };
|
|
54
|
+
assert.equal((await downloadNative('https://github.com/owner/repo/releases/download/v1/tool', { fetcher })).toString(), 'data');
|
|
55
|
+
assert.ok(seen.every(({ options }) => !Object.keys(options.headers).some(key => /authorization|token/i.test(key))));
|
|
56
|
+
await assert.rejects(downloadNative('http://github.com/file', { fetcher }), /Host/);
|
|
57
|
+
await assert.rejects(downloadNative('https://evil.example/file', { fetcher }), /GitHub/);
|
|
58
|
+
await assert.rejects(downloadNative('https://github.com/file', { fetcher: async () => new Response(null, { status: 302, headers: { location: 'https://evil.example/file' } }) }), /Host/);
|
|
59
|
+
await assert.rejects(downloadNative('https://github.com/file', { maxBytes: 2, fetcher: async () => new Response('four') }), /limite/);
|
|
60
|
+
await assert.rejects(downloadNative('https://github.com/file', { fetcher: async () => new Response('missing', { status: 404 }) }), /404/);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('native extraction accepts exact raw/tar/zip executable and rejects traversal and links', () => {
|
|
64
|
+
const raw = Buffer.from('raw binary'); assert.deepEqual(extractNative(raw, asset(raw, 'raw')), raw);
|
|
65
|
+
for (const format of ['tar.gz', 'zip']) {
|
|
66
|
+
const pack = format === 'tar.gz' ? tar : zip;
|
|
67
|
+
const archive = pack([{ name: 'LICENSE', bytes: Buffer.from('license') }, { name: 'tool', bytes: raw }]);
|
|
68
|
+
assert.deepEqual(extractNative(archive, asset(archive, format)), raw);
|
|
69
|
+
for (const name of ['../escape', '/absolute', 'C:/drive', 'folder\\escape']) {
|
|
70
|
+
const bad = pack([{ name }, { name: 'tool' }]); assert.throws(() => extractNative(bad, asset(bad, format)), /Caminho/);
|
|
71
|
+
}
|
|
72
|
+
const duplicate = pack([{ name: 'tool' }, { name: 'TOOL' }]); assert.throws(() => extractNative(duplicate, asset(duplicate, format)), /duplicada/);
|
|
73
|
+
const link = pack([{ name: 'link', type: '2', mode: 0o120777 }, { name: 'tool' }]); assert.throws(() => extractNative(link, asset(link, format)), /Links|Link/);
|
|
74
|
+
assert.throws(() => extractNative(archive, { ...asset(archive, format), sha256: '0'.repeat(64) }), /SHA-256/);
|
|
75
|
+
assert.throws(() => extractNative(archive, asset(archive, format, 'absent')), /ausente/);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('native receipts reject tampering and symbolic files without following them', async t => {
|
|
80
|
+
const root = fixture(t), bytes = Buffer.from('binary'), entry = config(bytes); const prefix = join(root, 'stage'); mkdirSync(prefix);
|
|
81
|
+
const receipt = await installNative(prefix, entry, platformKey(), { download: async () => bytes }); assert.equal(checkNative(prefix, entry, platformKey()).binarySha256, receipt.binarySha256);
|
|
82
|
+
const executable = join(prefix, receipt.executable); writeFileSync(executable, 'tampered'); assert.throws(() => checkNative(prefix, entry, platformKey()), /alterado/);
|
|
83
|
+
writeFileSync(executable, bytes); const outside = join(root, 'outside'); writeFileSync(outside, 'preserve'); rmSync(executable);
|
|
84
|
+
try { symlinkSync(outside, executable); } catch (error) { if (['EPERM', 'EACCES'].includes(error.code)) return; throw error; }
|
|
85
|
+
assert.throws(() => checkNative(prefix, entry, platformKey()), /simbolico/); assert.equal(readFileSync(outside, 'utf8'), 'preserve');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('native manager installs, reuses without network, updates reviewed pins and rolls back', { skip: process.platform === 'win32' }, async t => {
|
|
89
|
+
const root = fixture(t); let bytes = Buffer.from('#!/bin/sh\nif [ "$1" = "exit" ]; then exit 37; fi\necho 1.2.3\n'), installs = 0, fail = false;
|
|
90
|
+
let entry = config(bytes); const manager = new ToolManager({ root, catalog: { 'native-test': entry }, install: async prefix => { installs++; if (fail) throw Error('download failed'); await installNative(prefix, entry, platformKey(), { download: async () => bytes }); } });
|
|
91
|
+
const installed = await manager.install(entry.name); assert.equal(installed.version, '1.2.3'); assert.equal(manager.check(entry.name).version, '1.2.3'); assert.equal(manager.run(entry.name, ['exit']), 37);
|
|
92
|
+
manager.fetchRelease = async () => { throw Error('unexpected network'); }; assert.equal((await manager.install(entry.name)).changed, false); assert.equal(installs, 1);
|
|
93
|
+
const old = readFileSync(join(manager.local(entry.name), 'CURRENT.json'));
|
|
94
|
+
bytes = Buffer.from('#!/bin/sh\necho 2.0.0\n'); entry = config(bytes, '2.0.0'); manager.catalog[entry.name] = entry; manager.fetchRelease = async () => nativeRelease(entry, platformKey()); fail = true;
|
|
95
|
+
await assert.rejects(manager.update(entry.name), /download failed/); assert.deepEqual(readFileSync(join(manager.local(entry.name), 'CURRENT.json')), old);
|
|
96
|
+
fail = false; assert.equal((await manager.update(entry.name)).version, '2.0.0'); assert.equal(manager.check(entry.name).version, '2.0.0');
|
|
97
|
+
assert.ok(!existsSync(join(manager.local(entry.name), '.update-lock')));
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('native environment removes unrelated credentials while preserving host settings', () => {
|
|
101
|
+
const saved = process.env.NATIVE_TEST_TOKEN; process.env.NATIVE_TEST_TOKEN = 'secret';
|
|
102
|
+
try { assert.equal(toolEnvironment(config(), '/tmp/local').NATIVE_TEST_TOKEN, undefined); } finally { if (saved === undefined) delete process.env.NATIVE_TEST_TOKEN; else process.env.NATIVE_TEST_TOKEN = saved; }
|
|
103
|
+
});
|
|
@@ -4,13 +4,19 @@ description: Finalidade e criação de skills compartilhadas.
|
|
|
4
4
|
|
|
5
5
|
# Skills
|
|
6
6
|
|
|
7
|
-
São procedimentos reutilizáveis, organizados
|
|
8
|
-
|
|
7
|
+
São procedimentos reutilizáveis, organizados por assunto. Cada skill usa
|
|
8
|
+
`<grupo>/<nome>/SKILL.md` com nome e descrição no YAML. A skill orienta decisões;
|
|
9
|
+
o agente responsável pode acionar comandos, scripts e outros perfis conforme
|
|
10
|
+
as capacidades do cliente.
|
|
9
11
|
|
|
10
12
|
- `git`: operações de versionamento.
|
|
11
13
|
- `projeto`: configuração, criação e manutenção dos recursos.
|
|
12
14
|
- `produto`: PRD, experiência e design técnico.
|
|
13
15
|
- `specs`: especificações, roadmap e implementação.
|
|
16
|
+
- `seguranca`: [revisão](seguranca/revisar-seguranca/SKILL.md),
|
|
17
|
+
[autorização](seguranca/revisar-autorizacao/SKILL.md),
|
|
18
|
+
[ameaças](seguranca/modelar-ameacas/SKILL.md) e
|
|
19
|
+
[correção](seguranca/corrigir-vulnerabilidade/SKILL.md).
|
|
14
20
|
|
|
15
21
|
Use `criar-skill` quando uma tarefa recorrente precisar de decisões próprias;
|
|
16
22
|
use `criar-recurso` para encontrar um fluxo existente antes de criar outro.
|
|
@@ -21,4 +27,6 @@ Use [proximos-passos](projeto/proximos-passos/SKILL.md) para orientar a
|
|
|
21
27
|
continuidade conforme a fase e as pendências reais.
|
|
22
28
|
|
|
23
29
|
O [guia compartilhado](../INSTRUCTIONS.md) orienta o uso das skills.
|
|
30
|
+
As skills de segurança integram a instalação do kit; os scanners exigem
|
|
31
|
+
[preparo explícito](../commands/preparar-seguranca/COMMAND.md).
|
|
24
32
|
Ao alterar o inventário, sincronize os índices e apontamentos locais.
|
|
@@ -24,7 +24,8 @@ instalador compartilhado.
|
|
|
24
24
|
No preparo inicial, execute `preparar-ferramentas setup` pelo seletor:
|
|
25
25
|
instala e verifica todas as ferramentas compatíveis, sem atualizar as saudáveis.
|
|
26
26
|
Aceita seleção por nome/grupo; relate falhas e itens não aplicáveis.
|
|
27
|
-
Para
|
|
27
|
+
Para npm, `update` consulta `latest` oficial; ferramentas nativas de segurança
|
|
28
|
+
seguem o pin revisado em `TOOL.json`, sem atualização silenciosa para outro binário.
|
|
28
29
|
`install` reutiliza uma instalação saudável; `check` verifica sem rede;
|
|
29
30
|
`info` mostra a configuração; `run <nome> [argumentos]` executa sem instalar.
|
|
30
31
|
Sem ação, mostre ajuda. Não interprete ausência de argumentos como instalação.
|
|
@@ -41,6 +42,9 @@ Em `skills`, use `find` ou `add <origem> --list`; incorporações passam por
|
|
|
41
42
|
O shell virtual não substitui testes reais. Só ative proxy, certificados,
|
|
42
43
|
serviços ou gravação quando necessários à tarefa e com dependências preparadas.
|
|
43
44
|
|
|
45
|
+
Para segurança, use [preparar-seguranca](../../../commands/preparar-seguranca/COMMAND.md).
|
|
46
|
+
Preparar o CLI e atualizar bases são operações separadas da análise offline.
|
|
47
|
+
|
|
44
48
|
Antes de usar agent-desktop, leia `skills get desktop` e `skills get platform`
|
|
45
49
|
da versão instalada e confira `status`. Para agent-browser, leia
|
|
46
50
|
`skills get core` e `--help` da versão instalada e
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Segurança com scanners locais, triagem e provas de controles.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Segurança local
|
|
6
|
+
|
|
7
|
+
A instalação do kit inclui skills, regras, comandos e perfis de segurança.
|
|
8
|
+
Os scanners são preparados por uma ação explícita; os arquivos de instrução
|
|
9
|
+
não iniciam auditorias, serviços ou downloads em segundo plano.
|
|
10
|
+
|
|
11
|
+
## Como os componentes trabalham juntos
|
|
12
|
+
|
|
13
|
+
O comando [auditar-seguranca][audit] organiza o trabalho. A
|
|
14
|
+
[skill de revisão][review] define como analisar e o [auditor][auditor] faz a
|
|
15
|
+
triagem contextual. O script executa ferramentas e produz evidências; as
|
|
16
|
+
[regras][rules] orientam todas as etapas. Veja o [catálogo de comandos](../../commands/README.md).
|
|
17
|
+
|
|
18
|
+
| Ferramenta | Pergunta que ajuda a responder |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| [Gitleaks][gitleaks] | Há indícios de segredos nos arquivos analisados? |
|
|
21
|
+
| [OSV-Scanner][osv] | A base local conhece falhas nas versões das dependências? |
|
|
22
|
+
| [Opengrep][opengrep] | O código corresponde aos padrões das regras locais? |
|
|
23
|
+
|
|
24
|
+
As ferramentas executam localmente e não exigem conta ou assinatura.
|
|
25
|
+
Versões revisadas e suas origens ficam nos `TOOL.json`. Downloads públicos de
|
|
26
|
+
binários e bases são separados da análise. A política do cliente/modelo de IA
|
|
27
|
+
continua sendo própria; não coloque valores secretos no contexto da conversa.
|
|
28
|
+
|
|
29
|
+
## Preparar e executar
|
|
30
|
+
|
|
31
|
+
Use Node.js 24+ e o projeto autorizado. O fluxo de
|
|
32
|
+
[preparar-seguranca][prepare] instala os scanners e confere seu funcionamento:
|
|
33
|
+
|
|
34
|
+
```console
|
|
35
|
+
node .pbai4devs/scripts/preparar-ferramentas/SCRIPT.mjs setup security
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Para dependências, baixe a base dos ecossistemas realmente utilizados. Exemplo
|
|
39
|
+
para npm; substitua por `Go` ou pela seleção suportada que corresponda ao projeto:
|
|
40
|
+
|
|
41
|
+
```console
|
|
42
|
+
node .pbai4devs/scripts/auditar-seguranca/SCRIPT.mjs update-data --ecosystems npm
|
|
43
|
+
node .pbai4devs/scripts/auditar-seguranca/SCRIPT.mjs check
|
|
44
|
+
node .pbai4devs/scripts/auditar-seguranca/SCRIPT.mjs run
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`run` usa os três checks: `secrets`, `dependencies` e `sast`. Para limitar,
|
|
48
|
+
use [verificar-segredos][secrets] ou [verificar-dependencias][dependencies].
|
|
49
|
+
A análise usa dados locais; base ausente não autoriza enviar o projeto a uma API.
|
|
50
|
+
Confira no relatório a versão da ferramenta, a base e o escopo efetivamente lido.
|
|
51
|
+
|
|
52
|
+
## O que ainda precisa ser provado
|
|
53
|
+
|
|
54
|
+
O [artigo sobre falhas recorrentes][article] motiva os temas abaixo. As
|
|
55
|
+
verificações dependem da arquitetura e não equivalem a uma certificação.
|
|
56
|
+
|
|
57
|
+
| Tema | Prova necessária no projeto |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| Autorização no servidor | API direta recusa visitante e papel insuficiente. |
|
|
60
|
+
| Acesso por identificador | Usuário A não lê ou altera o recurso de B. |
|
|
61
|
+
| Isolamento dos dados | Policies/RLS ou Security Rules restringem o acesso. |
|
|
62
|
+
| Segredos | Credenciais privadas não chegam a fontes, bundles ou logs. |
|
|
63
|
+
| Entradas e abuso | Testes de validação, saída, upload e limites do fluxo. |
|
|
64
|
+
|
|
65
|
+
RLS é um controle de PostgreSQL usado pelo Supabase; Firebase tem Security Rules.
|
|
66
|
+
SDKs privilegiados podem atravessar esses controles e precisam de autorização
|
|
67
|
+
no servidor. Esconder um botão de administrador não protege a API.
|
|
68
|
+
|
|
69
|
+
Use [revisar-autorizacao][authorization] com identidades sintéticas distintas,
|
|
70
|
+
testes negativos e dados locais. Para entradas, prefira consultas parametrizadas,
|
|
71
|
+
codificação de saída por contexto e limites de upload; sanitização genérica não
|
|
72
|
+
resolve todos os casos. Teste a interface no navegador quando ela existir.
|
|
73
|
+
Sem aplicação executável, registre essas provas como **não verificadas**.
|
|
74
|
+
|
|
75
|
+
## Interpretar e corrigir
|
|
76
|
+
|
|
77
|
+
O runner retorna `0` sem achados, `1` com achados e `2` em falha ou verificação
|
|
78
|
+
incompleta. Leia o estado de cada check: não aplicável e não executado não
|
|
79
|
+
significam aprovado. Regras locais e formatos suportados limitam a cobertura.
|
|
80
|
+
|
|
81
|
+
O relatório fica em `.agent-tests/security/<execucao>/report.json`, sem valores
|
|
82
|
+
secretos. O auditor distingue suspeitas, falhas confirmadas e falsos positivos.
|
|
83
|
+
Use [modelar-ameacas][threats] para novos fluxos e
|
|
84
|
+
[corrigir-vulnerabilidade][fix] para reparar a causa com teste de regressão.
|
|
85
|
+
Uma credencial real exposta precisa de revogação pelo responsável; não a teste
|
|
86
|
+
contra serviços externos. Feche a entrega com [revisão independente][gate].
|
|
87
|
+
|
|
88
|
+
[Skills](../README.md) · [Ferramentas](../../tools/README.md)
|
|
89
|
+
|
|
90
|
+
[audit]: ../../commands/auditar-seguranca/COMMAND.md
|
|
91
|
+
[review]: ../../skills/seguranca/revisar-seguranca/SKILL.md
|
|
92
|
+
[auditor]: ../../agents/auditor-seguranca/AGENT.md
|
|
93
|
+
[rules]: ../../rules/seguranca/RULE.md
|
|
94
|
+
[gitleaks]: ../../tools/gitleaks/README.md
|
|
95
|
+
[osv]: ../../tools/osv-scanner/README.md
|
|
96
|
+
[opengrep]: ../../tools/opengrep/README.md
|
|
97
|
+
[prepare]: ../../commands/preparar-seguranca/COMMAND.md
|
|
98
|
+
[secrets]: ../../commands/verificar-segredos/COMMAND.md
|
|
99
|
+
[dependencies]: ../../commands/verificar-dependencias/COMMAND.md
|
|
100
|
+
[authorization]: ../../skills/seguranca/revisar-autorizacao/SKILL.md
|
|
101
|
+
[threats]: ../../skills/seguranca/modelar-ameacas/SKILL.md
|
|
102
|
+
[fix]: ../../skills/seguranca/corrigir-vulnerabilidade/SKILL.md
|
|
103
|
+
[gate]: ../../commands/revisar-entrega/COMMAND.md
|
|
104
|
+
[article]: https://manodeyvin.com.br/p/5-vacilacoes-de-seguranca-que-a-ia
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: corrigir-vulnerabilidade
|
|
3
|
+
description: Corrige um achado validado e prova que a causa foi removida.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Corrigir vulnerabilidade
|
|
7
|
+
|
|
8
|
+
Receba o achado e sua evidência de [revisar-seguranca](../revisar-seguranca/SKILL.md).
|
|
9
|
+
Confirme o caminho de abuso e o escopo autorizado. Esclareça dúvidas e apresente
|
|
10
|
+
o plano antes de editar, respeitando aprovações já dadas.
|
|
11
|
+
|
|
12
|
+
Corrija a causa, preserve o contrato legítimo e acrescente um teste de regressão
|
|
13
|
+
que falhe antes e passe depois. Não silencie regra ou exclua arquivo apenas
|
|
14
|
+
para deixar o scanner verde; documente falsos positivos com justificativa.
|
|
15
|
+
|
|
16
|
+
Use [implementar-spec](../../specs/implementar-spec/SKILL.md) quando houver spec
|
|
17
|
+
e [verificar-trabalho](../../projeto/verificar-trabalho/SKILL.md) para as provas.
|
|
18
|
+
Repita o scanner relevante e o cenário original. Mudanças web exigem navegador;
|
|
19
|
+
autorização exige a matriz de [permissões](../revisar-autorizacao/SKILL.md).
|
|
20
|
+
|
|
21
|
+
Se houver segredo real, não o reproduza. Informe o responsável para revogação
|
|
22
|
+
e rotação pelo canal apropriado; remover do código não invalida a credencial.
|
|
23
|
+
Não altere contas, histórico Git ou serviços externos sem escopo explícito.
|
|
24
|
+
|
|
25
|
+
Saída: correção, teste, risco residual e documentação atualizada. Feche com
|
|
26
|
+
[revisar-entrega](../../../commands/revisar-entrega/COMMAND.md), incluindo
|
|
27
|
+
revisão independente e evidências ignoradas em `.agent-tests/security/`.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: modelar-ameacas
|
|
3
|
+
description: Identifica ativos, fronteiras de confiança e ameaças do escopo aprovado.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Modelar ameaças
|
|
7
|
+
|
|
8
|
+
Leia produto, design e código relacionados. Reúna primeiro as dúvidas sobre
|
|
9
|
+
dados, atores, operações privilegiadas e limites do ambiente. Registre o que
|
|
10
|
+
é fato, hipótese ou informação ausente; não invente arquitetura.
|
|
11
|
+
|
|
12
|
+
Mapeie os ativos, entradas, fluxos e fronteiras de confiança. Para cada ameaça,
|
|
13
|
+
descreva pré-condição, caminho de abuso, impacto, controle e teste observável.
|
|
14
|
+
Priorize o que o desenho realmente permite: acesso entre usuários, elevação
|
|
15
|
+
de privilégio, vazamento, injeção e abuso de recursos.
|
|
16
|
+
|
|
17
|
+
Use o [modelo de revisão](../../../templates/revisao-seguranca/TEMPLATE.md)
|
|
18
|
+
como apoio e o [auditor](../../../agents/auditor-seguranca/AGENT.md) para revisão.
|
|
19
|
+
Se o projeto precisar de um documento permanente, combine seu destino com o
|
|
20
|
+
usuário e siga [atualizar-documentacao](../../projeto/atualizar-documentacao/SKILL.md).
|
|
21
|
+
Rascunhos e evidências ficam em `.agent-tests/security/`.
|
|
22
|
+
|
|
23
|
+
Saída: ameaças priorizadas, controles existentes, lacunas e critérios de teste.
|
|
24
|
+
O modelo orienta [revisar-seguranca](../revisar-seguranca/SKILL.md); não comprova
|
|
25
|
+
que os controles foram implementados ou testados.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: revisar-autorizacao
|
|
3
|
+
description: Verifica identidade, permissões por recurso e isolamento entre usuários.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Revisar autorização
|
|
7
|
+
|
|
8
|
+
Identifique sujeitos, papéis, recursos e operações permitidas. Leia rotas,
|
|
9
|
+
middleware, políticas de dados e testes; não deduza autorização pela interface.
|
|
10
|
+
Se o contrato não existir, reúna as dúvidas antes de propor mudanças.
|
|
11
|
+
|
|
12
|
+
Confira autenticação e autorização no servidor em cada operação sensível,
|
|
13
|
+
incluindo IDs manipuláveis, operações em lote e ações administrativas.
|
|
14
|
+
Em PostgreSQL/Supabase, revise RLS, policies, papéis e acessos privilegiados.
|
|
15
|
+
Em Firebase, revise Security Rules e SDKs privilegiados; são controles distintos.
|
|
16
|
+
|
|
17
|
+
Com contas sintéticas no ambiente local autorizado, prove no mínimo:
|
|
18
|
+
|
|
19
|
+
- Visitante não autenticado recebe a recusa prevista.
|
|
20
|
+
- Usuário A acessa seu recurso e não acessa o de B trocando o identificador.
|
|
21
|
+
- Usuário comum não obtém a ação administrativa pela API direta.
|
|
22
|
+
- Listagens, alterações e exclusões obedecem ao mesmo isolamento.
|
|
23
|
+
|
|
24
|
+
Use testes de API e, quando houver interface, navegador conforme
|
|
25
|
+
[verificar-trabalho](../../projeto/verificar-trabalho/SKILL.md).
|
|
26
|
+
Não crie contas reais nem use dados de produção para essa prova.
|
|
27
|
+
Sem aplicação executável ou identidades de teste, registre **não verificado**.
|
|
28
|
+
Entregue matriz de permissões, resultados e achados sem segredos em
|
|
29
|
+
`.agent-tests/security/`, usando o [modelo](../../../templates/revisao-seguranca/TEMPLATE.md).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: revisar-seguranca
|
|
3
|
+
description: Revisa riscos de segurança com análise local, contexto e provas.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Revisar segurança
|
|
7
|
+
|
|
8
|
+
Defina o escopo: mudança ou projeto, dados sensíveis, stack e ambiente autorizado.
|
|
9
|
+
Leia as regras de [segurança](../../../rules/seguranca/RULE.md) e
|
|
10
|
+
[execução local](../../../rules/execucao-local/RULE.md). Não amplie o alvo.
|
|
11
|
+
|
|
12
|
+
Use [auditar-seguranca](../../../commands/auditar-seguranca/COMMAND.md) para
|
|
13
|
+
os scanners disponíveis e o [auditor](../../../agents/auditor-seguranca/AGENT.md)
|
|
14
|
+
para interpretar os resultados. Uma ferramenta ausente ou base indisponível
|
|
15
|
+
gera pendência; resultado vazio não substitui análise de contexto.
|
|
16
|
+
|
|
17
|
+
Revise também autenticação, autorização por recurso, isolamento de dados,
|
|
18
|
+
segredos e entradas conforme o [guia](../REFERENCE.md).
|
|
19
|
+
Use [revisar-autorizacao](../revisar-autorizacao/SKILL.md) para permissões e
|
|
20
|
+
[modelar-ameacas](../modelar-ameacas/SKILL.md) quando houver novas fronteiras.
|
|
21
|
+
|
|
22
|
+
Registre achado, evidência, impacto, confiança e estado da triagem, sem valores
|
|
23
|
+
secretos. Diferencie vulnerabilidade confirmada, suspeita e falso positivo.
|
|
24
|
+
Prove cenários de API pelo terminal e fluxos web no navegador local autorizado,
|
|
25
|
+
seguindo [verificar-trabalho](../../projeto/verificar-trabalho/SKILL.md).
|
|
26
|
+
Salve o relatório em `.agent-tests/security/`; não declare o produto seguro
|
|
27
|
+
com base apenas em scanners. Encaminhe correções à skill correspondente.
|
|
@@ -28,6 +28,7 @@ enquanto o usuário estiver preparando somente a estrutura dos agentes.
|
|
|
28
28
|
| [Revisão de entrega][revisao] | Implementação | Antes de commit/merge. |
|
|
29
29
|
| [Release][release], [CHANGELOG][changelog] | Primeira entrega | Publicar. |
|
|
30
30
|
| [SECURITY][security] | Suporte público | Política/suporte mudar. |
|
|
31
|
+
| [Revisão de segurança][security-review] | Análise autorizada | Ameaças e provas. |
|
|
31
32
|
| [Comando][comando] | Recurso agêntico | Entrada ou fluxo mudar. |
|
|
32
33
|
|
|
33
34
|
Antes, inventarie e leia os documentos existentes com `atualizar-documentacao`.
|
|
@@ -65,4 +66,5 @@ Modelos, configurações e relatórios não são espelhados nas pastas dos agent
|
|
|
65
66
|
[release]: release/TEMPLATE.md
|
|
66
67
|
[changelog]: changelog/TEMPLATE.md
|
|
67
68
|
[security]: seguranca/TEMPLATE.md
|
|
69
|
+
[security-review]: revisao-seguranca/TEMPLATE.md
|
|
68
70
|
[comando]: comando/TEMPLATE.md
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Modelo de contexto, ameaças, achados e provas de segurança.
|
|
3
|
+
status: modelo
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Revisão de segurança
|
|
7
|
+
|
|
8
|
+
Preencha com fatos do escopo autorizado. Resultados e rascunhos ficam em
|
|
9
|
+
`.agent-tests/security/`; combine o destino de documentação permanente.
|
|
10
|
+
Este modelo complementa o relatório JSON dos scanners, sem substituir suas provas.
|
|
11
|
+
|
|
12
|
+
## Contexto
|
|
13
|
+
|
|
14
|
+
Alvo, alteração, ambiente, ativos, atores, operações e fronteiras de confiança.
|
|
15
|
+
Registre fontes, hipóteses e dúvidas; não preencha lacunas com suposições.
|
|
16
|
+
|
|
17
|
+
## Ameaças e controles
|
|
18
|
+
|
|
19
|
+
| Ameaça | Pré-condição | Impacto | Controle existente | Teste necessário |
|
|
20
|
+
| --- | --- | --- | --- | --- |
|
|
21
|
+
| A definir | A confirmar | A confirmar | Evidência | Resultado esperado |
|
|
22
|
+
|
|
23
|
+
## Achados
|
|
24
|
+
|
|
25
|
+
| ID | Local | Evidência sem segredo | Confiança | Estado | Próxima ação |
|
|
26
|
+
| --- | --- | --- | --- | --- | --- |
|
|
27
|
+
| SEC-001 | Arquivo/linha | Evidência | A avaliar | A triar | Responsável |
|
|
28
|
+
|
|
29
|
+
## Validação
|
|
30
|
+
|
|
31
|
+
Critério, comando ou cenário, resultado, caminho da prova e limitações.
|
|
32
|
+
Separe o que passou, falhou, não se aplica e não foi executado.
|
|
33
|
+
Registre revisão independente e risco residual antes de concluir.
|