security_pentest_planner 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,439 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecurityPentestPlanner
4
+ class VectorCatalog
5
+ Vector = Struct.new(
6
+ :id,
7
+ :name,
8
+ :category_slug,
9
+ :category_title,
10
+ :layer,
11
+ :triggers,
12
+ :objective,
13
+ :procedure,
14
+ :payload,
15
+ :payload_type,
16
+ :expected_validation,
17
+ :failure_criteria,
18
+ :risk,
19
+ keyword_init: true
20
+ )
21
+
22
+ Category = Struct.new(:slug, :title, :layer, keyword_init: true)
23
+
24
+ API_CATEGORIES = [
25
+ Category.new(slug: "controle-de-acesso", title: "Testes de Controle de Acesso (BOLA/IDOR)", layer: :api),
26
+ Category.new(slug: "ataques-de-injecao-e-manipulacao-de-params", title: "Ataques de Injeção e Manipulação de Parâmetros", layer: :api),
27
+ Category.new(slug: "seguranca-da-camada-de-negocio-e-logica", title: "Segurança da Camada de Negócio e Lógica", layer: :api),
28
+ Category.new(slug: "fuzzing-e-tratamento-de-erros", title: "Fuzzing e Tratamento de Erros", layer: :api)
29
+ ].freeze
30
+
31
+ DATALAKE_CATEGORIES = [
32
+ Category.new(slug: "testes-de-injecao-e-logica", title: "Testes de Injeção e Lógica", layer: :datalake),
33
+ Category.new(slug: "testes-de-controle-de-acesso", title: "Testes de Controle de Acesso (IAM & Permissões)", layer: :datalake),
34
+ Category.new(slug: "testes-de-performance-e-custo", title: "Testes de Performance e Custo (DoS de Data Lake)", layer: :datalake)
35
+ ].freeze
36
+
37
+ def self.vectors_for(analyzer)
38
+ new(analyzer).selected_vectors
39
+ end
40
+
41
+ def initialize(analyzer)
42
+ @analyzer = analyzer
43
+ @context = build_context
44
+ end
45
+
46
+ def selected_vectors
47
+ all_vectors.select { |vector| applicable?(vector) }.map { |vector| expand(vector) }
48
+ end
49
+
50
+ def grouped_vectors
51
+ selected_vectors.group_by { |v| [v.layer, v.category_slug] }
52
+ end
53
+
54
+ private
55
+
56
+ attr_reader :analyzer, :context
57
+
58
+ def build_context
59
+ endpoint = analyzer.primary_endpoint
60
+ {
61
+ team: "TIME",
62
+ tenant_header: analyzer.tenant_header_name,
63
+ endpoint_path: endpoint&.path || "/api/v1/resource",
64
+ method: endpoint&.method || "GET",
65
+ host: analyzer.host.gsub(%r{^https?://}, ""),
66
+ date_params: analyzer.date_param_names,
67
+ date_param_1: analyzer.date_param_names[0] || "start_date",
68
+ date_param_2: analyzer.date_param_names[1] || "end_date",
69
+ enum_param: analyzer.enum_param&.name || "channels",
70
+ enum_values: analyzer.enum_param&.enum_values&.join(", ") || "email, sms, whatsapp",
71
+ timezone_header: analyzer.timezone_headers.first&.name || "Timezone",
72
+ input_sources: analyzer.parser.source_path || "OpenAPI spec"
73
+ }
74
+ end
75
+
76
+ def applicable?(vector)
77
+ vector.triggers.all? { |trigger| analyzer.signals[trigger] }
78
+ end
79
+
80
+ def expand(vector)
81
+ Vector.new(
82
+ id: vector.id,
83
+ name: vector.name,
84
+ category_slug: vector.category_slug,
85
+ category_title: vector.category_title,
86
+ layer: vector.layer,
87
+ triggers: vector.triggers,
88
+ objective: interpolate(vector.objective),
89
+ procedure: vector.procedure.map { |step| interpolate(step) },
90
+ payload: interpolate(vector.payload),
91
+ payload_type: vector.payload_type,
92
+ expected_validation: interpolate(vector.expected_validation),
93
+ failure_criteria: interpolate(vector.failure_criteria),
94
+ risk: vector.risk
95
+ )
96
+ end
97
+
98
+ def interpolate(text)
99
+ text.gsub(/\{\{(\w+)\}\}/) { context[Regexp.last_match(1).to_sym] || "{{#{Regexp.last_match(1)}}}" }
100
+ end
101
+
102
+ def all_vectors
103
+ @all_vectors ||= api_vectors + datalake_vectors
104
+ end
105
+
106
+ def api_vectors
107
+ [
108
+ vector(
109
+ id: "tenant-traversal",
110
+ name: "Tenant Traversal",
111
+ category_slug: "controle-de-acesso",
112
+ category_title: API_CATEGORIES[0].title,
113
+ layer: :api,
114
+ triggers: [:tenant_scoping],
115
+ objective: "Substituir o header `{{tenant_header}}` por ID de outro cliente ou ID inexistente.",
116
+ procedure: [
117
+ "Obter baseline com tenant legítimo e intervalo de datas válido.",
118
+ "Repetir a requisição substituindo `{{tenant_header}}` por ID de tenant alheio.",
119
+ "Repetir com IDs inexistentes e valores extremamente longos."
120
+ ],
121
+ payload: "GET {{endpoint_path}}?{{date_param_1}}=2026-01-01&{{date_param_2}}=2026-01-31 HTTP/1.1\nHost: {{host}}\n{{tenant_header}}: 999999999999999999",
122
+ payload_type: "http",
123
+ expected_validation: "Sem retorno de estatísticas/dados de terceiros; preferencialmente métricas zeradas ou 403/404.",
124
+ failure_criteria: "200 com estatísticas ou dados de tenant não autorizado.",
125
+ risk: "BOLA/IDOR — Cross-Tenant Data Leak"
126
+ ),
127
+ vector(
128
+ id: "header-injection",
129
+ name: "Header Injection",
130
+ category_slug: "controle-de-acesso",
131
+ category_title: API_CATEGORIES[0].title,
132
+ layer: :api,
133
+ triggers: [:tenant_scoping],
134
+ objective: "Enviar headers conflitantes para verificar qual valor o backend prioriza.",
135
+ procedure: [
136
+ "Enviar `Tenant-ID` e `X-Tenant-ID` com valores distintos.",
137
+ "Inverter ordem dos headers e repetir.",
138
+ "Comparar resposta com baseline de cada tenant isolado."
139
+ ],
140
+ payload: "Tenant-ID: tenant-vitima\nX-Tenant-ID: tenant-atacante",
141
+ payload_type: "http",
142
+ expected_validation: "Comportamento determinístico; priorização segura do header canônico; sem vazamento do tenant vítima.",
143
+ failure_criteria: "Resposta alinhada ao tenant errado ou comportamento inconsistente.",
144
+ risk: "Bypass de isolamento por ambiguidade de parsing HTTP"
145
+ ),
146
+ vector(
147
+ id: "privilege-escalation",
148
+ name: "Privilege Escalation — omissão de tenant",
149
+ category_slug: "controle-de-acesso",
150
+ category_title: API_CATEGORIES[0].title,
151
+ layer: :api,
152
+ triggers: [:tenant_scoping],
153
+ objective: "Acessar o endpoint omitindo o header `{{tenant_header}}`.",
154
+ procedure: [
155
+ "Chamar `{{endpoint_path}}` sem `{{tenant_header}}`.",
156
+ "Verificar se query param alternativo de tenant é aceito.",
157
+ "Observar se a API retorna dados agregados cross-tenant."
158
+ ],
159
+ payload: "GET {{endpoint_path}}?{{date_param_1}}=2026-01-01&{{date_param_2}}=2026-01-31 HTTP/1.1\nHost: {{host}}",
160
+ payload_type: "http",
161
+ expected_validation: "400 Bad Request ou 401/403; nunca dados agregados cross-tenant.",
162
+ failure_criteria: "200 com métricas de negócio sem escopo de tenant.",
163
+ risk: "Visualização de dados de todos os clientes"
164
+ ),
165
+ vector(
166
+ id: "sqli-basic",
167
+ name: "SQL Injection — parâmetros de data",
168
+ category_slug: "ataques-de-injecao-e-manipulacao-de-params",
169
+ category_title: API_CATEGORIES[1].title,
170
+ layer: :api,
171
+ triggers: [:date_range],
172
+ objective: "Inserir payloads de escape em campos de data/filtro (`{{date_param_1}}`, `{{date_param_2}}`).",
173
+ procedure: [
174
+ "Substituir datas por payloads SQLi clássicos.",
175
+ "Observar código HTTP e corpo de erro.",
176
+ "Verificar se queries downstream permanecem inalteradas."
177
+ ],
178
+ payload: "{{date_param_1}}=2026-01-01' OR 1=1--\n{{date_param_2}}=2026-01-01' /*\n' OR '1'='1\n1; DROP TABLE users--",
179
+ payload_type: "txt",
180
+ expected_validation: "Sanitização ou rejeição; query inalterada; sem erro 500.",
181
+ failure_criteria: "Erro 500, stack trace SQL ou dados fora do escopo esperado.",
182
+ risk: "Injeção SQL via parâmetros de filtro"
183
+ ),
184
+ vector(
185
+ id: "enum-injection",
186
+ name: "Enum Injection",
187
+ category_slug: "ataques-de-injecao-e-manipulacao-de-params",
188
+ category_title: API_CATEGORIES[1].title,
189
+ layer: :api,
190
+ triggers: [:enum_params],
191
+ objective: "Enviar valores fora do enum/array homologado no parâmetro `{{enum_param}}`.",
192
+ procedure: [
193
+ "Enviar valores válidos como baseline ({{enum_values}}).",
194
+ "Testar `{{enum_param}}=all`, `{{enum_param}}=admin` e arrays malformados.",
195
+ "Verificar se o escopo da query permanece inalterado."
196
+ ],
197
+ payload: "{{enum_param}}=all\n{{enum_param}}=admin\n{{enum_param}}[]=invalid",
198
+ payload_type: "txt",
199
+ expected_validation: "Rejeição ou ignore seguro; escopo da query inalterado.",
200
+ failure_criteria: "Dados retornados fora do canal/escopo esperado.",
201
+ risk: "Bypass de filtro por enum corrompido"
202
+ ),
203
+ vector(
204
+ id: "timezone-manipulation",
205
+ name: "Timezone Manipulation",
206
+ category_slug: "ataques-de-injecao-e-manipulacao-de-params",
207
+ category_title: API_CATEGORIES[1].title,
208
+ layer: :api,
209
+ triggers: [:free_text_headers],
210
+ objective: "Explorar header `{{timezone_header}}` como string livre.",
211
+ procedure: [
212
+ "Enviar timezone inválido (ex: `Invalid/Zone`).",
213
+ "Testar CRLF injection no header.",
214
+ "Enviar buffer longo (8KB) para testar robustez do parser."
215
+ ],
216
+ payload: "{{timezone_header}}: Invalid/Zone\n{{timezone_header}}: America/Sao_Paulo\\r\\nInjected-Header: true\n{{timezone_header}}: AAAA...(buffer 8KB)",
217
+ payload_type: "txt",
218
+ expected_validation: "Whitelist IANA ou rejeição; sem crash; sem header injection reflexivo.",
219
+ failure_criteria: "Crash, 500 ou headers injetados refletidos na resposta.",
220
+ risk: "Header injection e parsing inseguro"
221
+ ),
222
+ vector(
223
+ id: "data-range-exhaustion",
224
+ name: "Data Range Exhaustion (DoS Lógico)",
225
+ category_slug: "seguranca-da-camada-de-negocio-e-logica",
226
+ category_title: API_CATEGORIES[2].title,
227
+ layer: :api,
228
+ triggers: [:date_range],
229
+ objective: "Solicitar intervalo de datas massivo para forçar processamento pesado.",
230
+ procedure: [
231
+ "Enviar `{{date_param_1}}=1900-01-01` e `{{date_param_2}}=2100-12-31`.",
232
+ "Medir tempo de resposta e consumo downstream.",
233
+ "Verificar se há limite máximo de intervalo."
234
+ ],
235
+ payload: "{{date_param_1}}=1900-01-01\n{{date_param_2}}=2100-12-31",
236
+ payload_type: "txt",
237
+ expected_validation: "Limite máximo de intervalo; timeout controlado; 400 com mensagem clara.",
238
+ failure_criteria: "200 com processamento completo de range abusivo ou timeout sem controle.",
239
+ risk: "DoS lógico no banco / motor analítico"
240
+ ),
241
+ vector(
242
+ id: "negative-values",
243
+ name: "Negative Values — datas invertidas",
244
+ category_slug: "seguranca-da-camada-de-negocio-e-logica",
245
+ category_title: API_CATEGORIES[2].title,
246
+ layer: :api,
247
+ triggers: [:date_range],
248
+ objective: "Enviar data de início posterior à data de fim.",
249
+ procedure: [
250
+ "Enviar `{{date_param_1}}=2026-12-31` e `{{date_param_2}}=2026-01-01`.",
251
+ "Observar código HTTP e consistência dos resultados."
252
+ ],
253
+ payload: "{{date_param_1}}=2026-12-31\n{{date_param_2}}=2026-01-01",
254
+ payload_type: "txt",
255
+ expected_validation: "Rejeição com erro controlado; sem 500; sem resultados inconsistentes.",
256
+ failure_criteria: "500 ou resultados incoerentes retornados.",
257
+ risk: "Lógica de negócio frágil em validação cronológica"
258
+ ),
259
+ vector(
260
+ id: "rate-limit-absence",
261
+ name: "Rate Limit Absence",
262
+ category_slug: "seguranca-da-camada-de-negocio-e-logica",
263
+ category_title: API_CATEGORIES[2].title,
264
+ layer: :api,
265
+ triggers: [:analytics],
266
+ objective: "Verificar proteção contra abuso volumétrico em endpoints analíticos.",
267
+ procedure: [
268
+ "Enviar N requisições paralelas (ex: 50 em 10s) com payload válido.",
269
+ "Observar se há throttling por tenant/usuário.",
270
+ "Documentar ausência como item de hardening se não houver 429."
271
+ ],
272
+ payload: "50 requisições GET {{endpoint_path}} em 10 segundos",
273
+ payload_type: "txt",
274
+ expected_validation: "429 Too Many Requests ou throttling por tenant/usuário.",
275
+ failure_criteria: "Todas as requisições processadas sem limitação.",
276
+ risk: "DoS volumétrico em endpoint analítico"
277
+ ),
278
+ vector(
279
+ id: "verbosity-check",
280
+ name: "Verbosity Check",
281
+ category_slug: "fuzzing-e-tratamento-de-erros",
282
+ category_title: API_CATEGORIES[3].title,
283
+ layer: :api,
284
+ triggers: [:enum_params],
285
+ objective: "Provocar erros 400/404 e inspecionar corpo da resposta.",
286
+ procedure: [
287
+ "Path inexistente → 404.",
288
+ "Parâmetro malformado → 400.",
289
+ "Body JSON inválido → 400 (se aplicável)."
290
+ ],
291
+ payload: "GET /api/v1/inexistente HTTP/1.1\nGET {{endpoint_path}}?{{date_param_1}}=INVALID HTTP/1.1",
292
+ payload_type: "http",
293
+ expected_validation: "Mensagens genéricas; sem stack trace, paths internos ou nomes de colunas.",
294
+ failure_criteria: "Vazamento de stack trace, `/usr/src/app`, nomes de tabelas ou versões de framework.",
295
+ risk: "Information disclosure via error handling"
296
+ ),
297
+ vector(
298
+ id: "type-juggling",
299
+ name: "Type Juggling",
300
+ category_slug: "fuzzing-e-tratamento-de-erros",
301
+ category_title: API_CATEGORIES[3].title,
302
+ layer: :api,
303
+ triggers: [:enum_params],
304
+ objective: "Enviar tipos inesperados onde se espera array/object/primitive em `{{enum_param}}`.",
305
+ procedure: [
306
+ "Enviar boolean, hash, null e integer no lugar de array.",
307
+ "Observar código HTTP e robustez do parser JSON."
308
+ ],
309
+ payload: "{ \"{{enum_param}}\": true }\n{ \"{{enum_param}}\": { \"value\": \"email\" } }\n{ \"{{enum_param}}\": null }",
310
+ payload_type: "json",
311
+ expected_validation: "Parser JSON robusto; 400 controlado; sem 500.",
312
+ failure_criteria: "500 ou execução parcial de lógica de negócio.",
313
+ risk: "Type confusion em parsing de entrada"
314
+ )
315
+ ]
316
+ end
317
+
318
+ def datalake_vectors
319
+ [
320
+ vector(
321
+ id: "bypass-tenant",
322
+ name: "Bypass de Tenant",
323
+ category_slug: "testes-de-injecao-e-logica",
324
+ category_title: DATALAKE_CATEGORIES[0].title,
325
+ layer: :datalake,
326
+ triggers: [:datalake],
327
+ objective: "Omitir ou neutralizar filtro de tenant na query final.",
328
+ procedure: [
329
+ "Executar query sem cláusula WHERE tenant_id.",
330
+ "Forçar bypass lógico com `OR 1=1`.",
331
+ "Verificar se row-level security impede cross-tenant."
332
+ ],
333
+ payload: "SELECT count(*) FROM `project.dataset.table`;\n\nSELECT count(*) FROM `project.dataset.table`\nWHERE tenant_id = @tenant_id OR 1=1;",
334
+ payload_type: "sql",
335
+ expected_validation: "Impossível cruzar dados de múltiplos tenants; parametrização enforced.",
336
+ failure_criteria: "Contagem ou dados de múltiplos tenants retornados.",
337
+ risk: "Cross-Tenant Data Leak no Data Lake"
338
+ ),
339
+ vector(
340
+ id: "union-exploitation",
341
+ name: "Union Exploitation",
342
+ category_slug: "testes-de-injecao-e-logica",
343
+ category_title: DATALAKE_CATEGORIES[0].title,
344
+ layer: :datalake,
345
+ triggers: [:datalake],
346
+ objective: "Injetar UNION apontando para metadados ou tabelas sensíveis.",
347
+ procedure: [
348
+ "Simular input malicioso chegando à camada SQL.",
349
+ "Tentar UNION ALL SELECT em INFORMATION_SCHEMA.",
350
+ "Verificar permissões da Service Account."
351
+ ],
352
+ payload: "SELECT cols FROM `table` WHERE tenant_id = @tenant_id\nUNION ALL SELECT table_name, column_name, data_type\nFROM `project.dataset.INFORMATION_SCHEMA.COLUMNS`;",
353
+ payload_type: "sql",
354
+ expected_validation: "SA sem permissão de leitura em INFORMATION_SCHEMA alheio; query rejeitada.",
355
+ failure_criteria: "Metadados de tabelas sensíveis retornados.",
356
+ risk: "Schema enumeration via SQL injection"
357
+ ),
358
+ vector(
359
+ id: "sa-overprivilege",
360
+ name: "Service Account Overprivilege",
361
+ category_slug: "testes-de-controle-de-acesso",
362
+ category_title: DATALAKE_CATEGORIES[1].title,
363
+ layer: :datalake,
364
+ triggers: [:datalake],
365
+ objective: "Verificar se a Service Account da API pode executar DDL/DML.",
366
+ procedure: [
367
+ "Tentar CREATE TABLE, DROP TABLE, INSERT, UPDATE, DELETE.",
368
+ "Verificar roles atribuídas (bigquery.dataViewer, jobUser)."
369
+ ],
370
+ payload: "CREATE TABLE test_pentest (id INT64);\nDROP TABLE `project.dataset.table`;\nINSERT INTO `table` VALUES (1);",
371
+ payload_type: "sql",
372
+ expected_validation: "SA estritamente Read-Only; DDL/DML rejeitados.",
373
+ failure_criteria: "Qualquer operação de escrita executada com sucesso.",
374
+ risk: "Comprometimento de integridade de dados"
375
+ ),
376
+ vector(
377
+ id: "metadata-leak",
378
+ name: "Metadata Leak",
379
+ category_slug: "testes-de-controle-de-acesso",
380
+ category_title: DATALAKE_CATEGORIES[1].title,
381
+ layer: :datalake,
382
+ triggers: [:datalake],
383
+ objective: "Mapear estrutura física via metadados (INFORMATION_SCHEMA).",
384
+ procedure: [
385
+ "Consultar INFORMATION_SCHEMA.COLUMNS e TABLES.",
386
+ "Verificar se SA expõe clusterização/particionamento.",
387
+ "Documentar como ATENÇÃO se leitura permitida sem vazamento de dados de negócio."
388
+ ],
389
+ payload: "SELECT * FROM `project.dataset.INFORMATION_SCHEMA.COLUMNS` WHERE table_name LIKE '%';\nSELECT ddl FROM `project.dataset.INFORMATION_SCHEMA.TABLES`;",
390
+ payload_type: "sql",
391
+ expected_validation: "Mínimo privilégio; SA não expõe metadados sensíveis via API.",
392
+ failure_criteria: "DDL completo ou esquema físico exposto a atacante.",
393
+ risk: "Information disclosure de metadados"
394
+ ),
395
+ vector(
396
+ id: "cartesian-product",
397
+ name: "Cartesian Product (Cross Join)",
398
+ category_slug: "testes-de-performance-e-custo",
399
+ category_title: DATALAKE_CATEGORIES[2].title,
400
+ layer: :datalake,
401
+ triggers: [:datalake],
402
+ objective: "Forçar cross join entre tabelas grandes para exaurir cotas.",
403
+ procedure: [
404
+ "Executar CROSS JOIN entre tabelas de alto volume.",
405
+ "Monitorar bytes billed e tempo de execução."
406
+ ],
407
+ payload: "SELECT a.*, b.*\nFROM `big_table` a\nCROSS JOIN `big_table` b\nWHERE a.tenant_id = @tenant_id\nLIMIT 100;",
408
+ payload_type: "sql",
409
+ expected_validation: "Timeout, quota exceeded ou bloqueio por policy; custo monitorado.",
410
+ failure_criteria: "Query executada com custo financeiro significativo sem alerta.",
411
+ risk: "DoS financeiro no Data Lake"
412
+ ),
413
+ vector(
414
+ id: "full-scan",
415
+ name: "Full Scan Forçado",
416
+ category_slug: "testes-de-performance-e-custo",
417
+ category_title: DATALAKE_CATEGORIES[2].title,
418
+ layer: :datalake,
419
+ triggers: [:datalake],
420
+ objective: "Query que ignora coluna de partição.",
421
+ procedure: [
422
+ "Executar COUNT sem filtro de partição.",
423
+ "Comparar bytes billed com query particionada.",
424
+ "Verificar se API sempre injeta filtro de partição."
425
+ ],
426
+ payload: "SELECT count(*) FROM `partitioned_table` WHERE tenant_id = @tenant_id;\n\n-- Comparar com:\nSELECT count(*) FROM `partitioned_table`\nWHERE tenant_id = @tenant_id AND partition_date BETWEEN '2026-01-01' AND '2026-01-31';",
427
+ payload_type: "sql",
428
+ expected_validation: "API sempre injeta filtro de partição; bytes billed controlados.",
429
+ failure_criteria: "Full scan de tabela particionada sem restrição.",
430
+ risk: "DoS de custo por scan completo"
431
+ )
432
+ ]
433
+ end
434
+
435
+ def vector(**attrs)
436
+ Vector.new(**attrs)
437
+ end
438
+ end
439
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecurityPentestPlanner
4
+ VERSION = "1.0.1"
5
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "security_pentest_planner/version"
4
+ require_relative "security_pentest_planner/errors"
5
+ require_relative "security_pentest_planner/openapi_parser"
6
+ require_relative "security_pentest_planner/contract_analyzer"
7
+ require_relative "security_pentest_planner/vector_catalog"
8
+ require_relative "security_pentest_planner/plan_renderer"
9
+ require_relative "security_pentest_planner/planner"
10
+ require_relative "security_pentest_planner/cli"
11
+
12
+ module SecurityPentestPlanner
13
+ class << self
14
+ def generate(input_path:, **options)
15
+ Planner.new(input_path: input_path, **options).call
16
+ end
17
+
18
+ def generate_from_spec(spec:, **options)
19
+ Planner.new(spec: spec, **options).call
20
+ end
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: security_pentest_planner
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Saulo Filho
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rake
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '13.0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '13.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rspec
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.12'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.12'
40
+ description: |
41
+ Ruby gem que analisa contratos OpenAPI/Swagger e gera Planos de Ação de Pentest
42
+ estruturados com vetores OWASP API Security, procedimentos, payloads, critérios
43
+ de validação e recomendações de remediação para sistemas multi-tenant e Data Lake.
44
+ email:
45
+ - saulofilho@users.noreply.github.com
46
+ executables:
47
+ - security-pentest-planner
48
+ extensions: []
49
+ extra_rdoc_files: []
50
+ files:
51
+ - CHANGELOG.md
52
+ - LICENSE.txt
53
+ - README.md
54
+ - exe/security-pentest-planner
55
+ - lib/security_pentest_planner.rb
56
+ - lib/security_pentest_planner/cli.rb
57
+ - lib/security_pentest_planner/contract_analyzer.rb
58
+ - lib/security_pentest_planner/errors.rb
59
+ - lib/security_pentest_planner/openapi_parser.rb
60
+ - lib/security_pentest_planner/plan_renderer.rb
61
+ - lib/security_pentest_planner/planner.rb
62
+ - lib/security_pentest_planner/vector_catalog.rb
63
+ - lib/security_pentest_planner/version.rb
64
+ homepage: https://saulofilho.github.io/security-pentest-planner/
65
+ licenses:
66
+ - MIT
67
+ metadata:
68
+ homepage_uri: https://saulofilho.github.io/security-pentest-planner/
69
+ source_code_uri: https://github.com/saulofilho/security-pentest-planner
70
+ rubygems_mfa_required: 'true'
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: 3.0.0
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubygems_version: 3.7.1
86
+ specification_version: 4
87
+ summary: Gera Planos de Ação de Pentest a partir de especificações OpenAPI
88
+ test_files: []