vert-core 1.0.0 → 1.0.2
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.
- checksums.yaml +4 -4
- data/README.md +8 -2
- data/SECURITY_AND_QUALITY_AUDIT.md +98 -0
- data/lib/vert/authorization/permission_resolver.rb +4 -1
- data/lib/vert/configuration.rb +1 -0
- data/lib/vert/version.rb +1 -1
- data/lib/vert_core.rb +5 -0
- data/vert.gemspec +7 -6
- metadata +28 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3cd14af74495d17174f61ed390691b90f01b5897ffc368e804cc6155ffc7e70d
|
|
4
|
+
data.tar.gz: 0fdad12a722f1f876349b3a1f93cef84a1307a34fb96f2587ab429d0b6d4320f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ca0ffeea71830f7f2f26028bd4b8bf269ed7d15138f7971846c72d15bb8b70ef9dfedc6afa118e98f7ecbbe63ff1dc8bdf15782836ffb62b216086e87006772d
|
|
7
|
+
data.tar.gz: 8427a308b31890c81df7cc7bcbd2ba7a867e8067bede7e45ad5e16c19f6880d9a3f27be033863467ad960dc98e901708c27b86277d6d7b65d79ef9cdda538d9b
|
data/README.md
CHANGED
|
@@ -4,10 +4,10 @@ Gem genérica de padrões para aplicações Rails: contexto de request, RLS, Out
|
|
|
4
4
|
|
|
5
5
|
## Instalação
|
|
6
6
|
|
|
7
|
-
Adicione ao `Gemfile
|
|
7
|
+
Adicione ao `Gemfile` (nome da gem no RubyGems é `vert-core`; o módulo continua `Vert`):
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
|
-
gem "vert"
|
|
10
|
+
gem "vert-core"
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Execute:
|
|
@@ -121,6 +121,12 @@ class MyJob < ApplicationJob
|
|
|
121
121
|
end
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
+
## Produção e segurança
|
|
125
|
+
|
|
126
|
+
- Defina sempre via **variáveis de ambiente** em produção: `RABBITMQ_URL`, `DOCUMENT_SERVICE_URL`, `REDIS_URL`. Os valores padrão (guest/localhost) são apenas para desenvolvimento.
|
|
127
|
+
- Serviços que publicam mensagens nas filas consumidas com `Vert::Rls::ConsumerContext` podem definir o contexto (tenant_id, user_id) via headers; garanta que apenas serviços confiáveis publiquem nessas filas.
|
|
128
|
+
- Para mais detalhes, veja `SECURITY_AND_QUALITY_AUDIT.md` no repositório.
|
|
129
|
+
|
|
124
130
|
## Licença
|
|
125
131
|
|
|
126
132
|
MIT.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Auditoria de Qualidade e Segurança — Gem vert-core
|
|
2
|
+
|
|
3
|
+
**Data:** 2025-03
|
|
4
|
+
**Escopo:** `services/00-gems/vert` (publicada no RubyGems como `vert-core`)
|
|
5
|
+
**Objetivo:** Garantir segurança e confiabilidade para uso em produção.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Resumo executivo
|
|
10
|
+
|
|
11
|
+
| Categoria | Status | Observação |
|
|
12
|
+
|------------------|--------|------------|
|
|
13
|
+
| Segurança | ✅ Aprovado com ressalvas | Sem vulnerabilidades críticas; ver itens 2.x |
|
|
14
|
+
| Qualidade | ✅ Bom | Código consistente; ver itens 3.x |
|
|
15
|
+
| Dependências | ✅ OK | Rails 7–9, Sidekiq, Bunny, Discard — versões declaradas |
|
|
16
|
+
| Publicação | ⚠️ Ajuste | CI usa Ruby 2.6; gemspec exige >= 3.2 |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 2. Segurança
|
|
21
|
+
|
|
22
|
+
### 2.1 Pontos positivos
|
|
23
|
+
|
|
24
|
+
- **SQL:** Uso de `ActiveRecord::Base.sanitize_sql` em `ConnectionHandler` para `SET LOCAL` (tenant_id, company_id, user_id). Nenhuma concatenação direta de input em SQL.
|
|
25
|
+
- **Autorização:** Queries em `PermissionResolver` usam placeholders (`?`). Nenhum `eval`, `YAML.load` ou `Marshal.load` em dados de usuário ou mensagens.
|
|
26
|
+
- **Contexto de job:** `Vert::Current.serialize/deserialize` só manipula Hash com atributos primitivos (tenant_id, user_id, company_id, request_id). Não há deserialização insegura.
|
|
27
|
+
- **Constantes:** `Object.const_get` em `PolicyFinder` e `OutboxEvent` usa apenas nomes derivados do modelo/record da aplicação, não de input do usuário.
|
|
28
|
+
- **Secrets:** Nenhuma senha, token ou API key hardcoded. URLs e credenciais vêm de `Vert.config` ou `ENV`.
|
|
29
|
+
|
|
30
|
+
### 2.2 Riscos e recomendações
|
|
31
|
+
|
|
32
|
+
| Risco | Severidade | Recomendação |
|
|
33
|
+
|-------|------------|--------------|
|
|
34
|
+
| **Defaults de conexão** | Baixo | `Configuration` usa `ENV.fetch(..., "amqp://guest:guest@localhost:5672/")` e `redis://localhost:6379/0`. Em produção, garantir sempre variáveis de ambiente; considerar remover defaults “guest” em doc ou comentar que são apenas para desenvolvimento. |
|
|
35
|
+
| **Document service** | Baixo | `DocumentServiceClient` não valida `base_url`. Garantir que `document_service_url` não seja controlada por usuário (evitar SSRF). Em produção usar HTTPS. |
|
|
36
|
+
| **Health / rotas** | Baixo | `Vert::Health::Routes.mount(router, path: nil)` usa `path` em `scope path`. O valor vem de `Vert.config.health_check_path` (default `/health`). Se a app permitir config dinâmico a partir de input, poderia haver path traversal; uso normal é seguro. |
|
|
37
|
+
| **Resposta 403** | Info | `render_forbidden` expõe `permission` (nome do método da policy) e `resource` (nome da classe). Pode ser desejável em APIs internas; em APIs públicas considerar resposta genérica para não revelar estrutura. |
|
|
38
|
+
| **Cache de permissões (Redis)** | Baixo | `invalidate_user_cache(user_id)` monta a chave `vert:permissions:#{user_id}:*`. Se `user_id` puder conter `*` ou `?`, o padrão Redis poderia invalidar mais chaves. Recomendação: usar apenas IDs válidos (UUID/integer) ou escapar/sanitizar. |
|
|
39
|
+
| **Consumers RabbitMQ** | Médio (contexto) | `ConsumerContext` define contexto (tenant_id, user_id, company_id) a partir dos headers da mensagem. Qualquer produtor da fila pode “personificar” um tenant. Garantir que apenas serviços confiáveis publiquem nessas filas e que a rede do broker seja controlada. |
|
|
40
|
+
|
|
41
|
+
### 2.3 O que não foi encontrado
|
|
42
|
+
|
|
43
|
+
- Uso de `eval`, `exec`, `system`, backticks ou `Kernel#open` com input externo.
|
|
44
|
+
- `YAML.load` ou `Marshal.load` em dados de request ou mensagens.
|
|
45
|
+
- Exposição de stack trace ou detalhes internos em respostas de erro (apenas mensagens controladas).
|
|
46
|
+
- Credenciais ou tokens fixos no código.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 3. Qualidade
|
|
51
|
+
|
|
52
|
+
### 3.1 Estrutura e convenções
|
|
53
|
+
|
|
54
|
+
- `# frozen_string_literal: true` e estilo consistente.
|
|
55
|
+
- Separação clara: concerns, clients, RLS, outbox, health, authorization.
|
|
56
|
+
- Entry point único: `vert_core.rb` → `vert.rb`; Railtie condicional.
|
|
57
|
+
|
|
58
|
+
### 3.2 Tratamento de erros
|
|
59
|
+
|
|
60
|
+
- `ConnectionHandler`: `rescue StandardError` com log e re-raise em `set_context`; em `reset_context` apenas log (evita mascarar falhas de reset).
|
|
61
|
+
- `DocumentServiceClient`: `execute_request` retorna hash `{ success:, error:, ... }`; não propaga exceção para o caller, adequado para cliente HTTP.
|
|
62
|
+
- `Publisher#publish`: `rescue StandardError` → `mark_as_failed!` e retorno `false`.
|
|
63
|
+
- Health checks: cada check em `check_all` em bloco próprio com `rescue` para não derrubar os demais.
|
|
64
|
+
|
|
65
|
+
### 3.3 Configuração
|
|
66
|
+
|
|
67
|
+
- Flags explícitas (enable_rls, enable_outbox, etc.) com default seguro (maioria `false`; health `true`).
|
|
68
|
+
- Documentação no README e no template do initializer.
|
|
69
|
+
|
|
70
|
+
### 3.4 Melhorias sugeridas
|
|
71
|
+
|
|
72
|
+
- **Configuração sensível:** Considerar um método `Vert.config.to_s` ou inspetor que oculte `rabbitmq_url` e `document_service_url` em logs (ex.: mostrar só host/scheme).
|
|
73
|
+
- **Versão do Ruby no CI:** Alinhar o workflow ao `required_ruby_version` (>= 3.2) para evitar publicar com Ruby incompatível.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 4. Gemspec e publicação
|
|
78
|
+
|
|
79
|
+
- **Nome:** `vert-core` (require: `vert_core`); módulo: `Vert`. Consistente.
|
|
80
|
+
- **Licença:** MIT. Adequado.
|
|
81
|
+
- **Dependências:** activesupport/activerecord 7–9, bunny ~> 2.22, discard ~> 1.3, sidekiq 7–9. Ranges claros.
|
|
82
|
+
- **CI:** `.github/workflows/gem-push.yml` usa Ruby 2.6; o gemspec exige `>= 3.2.0`. **Correção necessária:** usar Ruby 3.2 (ou 3.3) no workflow.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 5. Checklist pós-auditoria
|
|
87
|
+
|
|
88
|
+
- [ ] Atualizar workflow para Ruby 3.2+.
|
|
89
|
+
- [ ] Documentar na README que defaults de RabbitMQ/Redis são apenas para desenvolvimento.
|
|
90
|
+
- [ ] (Opcional) Sanitizar/validar `user_id` em chaves de cache no `PermissionResolver`.
|
|
91
|
+
- [ ] (Opcional) Resposta 403 mais genérica em ambientes públicos.
|
|
92
|
+
- [ ] Manter dependências atualizadas (`bundle audit` / Dependabot).
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 6. Conclusão
|
|
97
|
+
|
|
98
|
+
A gem **vert-core** está em bom estado para uso em produção do ponto de vista de segurança e qualidade. Não foram identificadas vulnerabilidades críticas ou altas. As recomendações são sobretudo de configuração (ENV em produção, confiança em produtores de mensagens), alinhamento do CI ao Ruby 3.2+ e pequenos ajustes opcionais de hardening e documentação.
|
|
@@ -51,7 +51,10 @@ module Vert
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def invalidate_user_cache(user_id)
|
|
54
|
-
|
|
54
|
+
return if user_id.blank?
|
|
55
|
+
# Sanitize to avoid Redis KEYS pattern injection (e.g. * or ? in user_id)
|
|
56
|
+
safe_id = user_id.to_s.gsub(%r{[*?\[\]\{\}\\]}, "")
|
|
57
|
+
pattern = "#{CACHE_PREFIX}:#{safe_id}:*"
|
|
55
58
|
redis_delete_pattern(pattern)
|
|
56
59
|
end
|
|
57
60
|
|
data/lib/vert/configuration.rb
CHANGED
|
@@ -50,6 +50,7 @@ module Vert
|
|
|
50
50
|
@enable_company_scoped = false
|
|
51
51
|
@enable_document_storeable = false
|
|
52
52
|
|
|
53
|
+
# Production: set RABBITMQ_URL, DOCUMENT_SERVICE_URL, REDIS_URL via ENV; do not rely on defaults.
|
|
53
54
|
@rls_user = ENV.fetch("RLS_USER", "app_user")
|
|
54
55
|
@rabbitmq_url = ENV.fetch("RABBITMQ_URL", "amqp://guest:guest@localhost:5672/")
|
|
55
56
|
@exchange_name = ENV.fetch("RABBITMQ_EXCHANGE", "vert.events")
|
data/lib/vert/version.rb
CHANGED
data/lib/vert_core.rb
ADDED
data/vert.gemspec
CHANGED
|
@@ -15,13 +15,14 @@ Gem::Specification.new do |spec|
|
|
|
15
15
|
spec.required_ruby_version = ">= 3.2.0"
|
|
16
16
|
|
|
17
17
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
-
spec.metadata["source_code_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = "#{spec.homepage}/tree/main"
|
|
19
19
|
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
20
20
|
|
|
21
21
|
spec.files = Dir.chdir(__dir__) do
|
|
22
22
|
Dir.glob("**/*").reject do |f|
|
|
23
23
|
File.directory?(f) ||
|
|
24
24
|
(File.expand_path(f) == __FILE__) ||
|
|
25
|
+
f.end_with?(".gem") ||
|
|
25
26
|
f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
|
|
26
27
|
end
|
|
27
28
|
end
|
|
@@ -29,11 +30,11 @@ Gem::Specification.new do |spec|
|
|
|
29
30
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
30
31
|
spec.require_paths = ["lib"]
|
|
31
32
|
|
|
32
|
-
spec.
|
|
33
|
-
spec.
|
|
34
|
-
spec.
|
|
35
|
-
spec.
|
|
36
|
-
spec.
|
|
33
|
+
spec.add_runtime_dependency "activesupport", ">= 7.0", "< 9.0"
|
|
34
|
+
spec.add_runtime_dependency "activerecord", ">= 7.0", "< 9.0"
|
|
35
|
+
spec.add_runtime_dependency "bunny", "~> 2.22"
|
|
36
|
+
spec.add_runtime_dependency "discard", "~> 1.3"
|
|
37
|
+
spec.add_runtime_dependency "sidekiq", ">= 7.0", "< 9.0"
|
|
37
38
|
|
|
38
39
|
spec.add_development_dependency "rake", "~> 13.0"
|
|
39
40
|
spec.add_development_dependency "rspec", "~> 3.0"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vert-core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vert Team
|
|
@@ -16,6 +16,9 @@ dependencies:
|
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
18
|
version: '7.0'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '9.0'
|
|
19
22
|
type: :runtime
|
|
20
23
|
prerelease: false
|
|
21
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -23,6 +26,9 @@ dependencies:
|
|
|
23
26
|
- - ">="
|
|
24
27
|
- !ruby/object:Gem::Version
|
|
25
28
|
version: '7.0'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '9.0'
|
|
26
32
|
- !ruby/object:Gem::Dependency
|
|
27
33
|
name: activerecord
|
|
28
34
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -30,6 +36,9 @@ dependencies:
|
|
|
30
36
|
- - ">="
|
|
31
37
|
- !ruby/object:Gem::Version
|
|
32
38
|
version: '7.0'
|
|
39
|
+
- - "<"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '9.0'
|
|
33
42
|
type: :runtime
|
|
34
43
|
prerelease: false
|
|
35
44
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -37,34 +46,37 @@ dependencies:
|
|
|
37
46
|
- - ">="
|
|
38
47
|
- !ruby/object:Gem::Version
|
|
39
48
|
version: '7.0'
|
|
49
|
+
- - "<"
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '9.0'
|
|
40
52
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name:
|
|
53
|
+
name: bunny
|
|
42
54
|
requirement: !ruby/object:Gem::Requirement
|
|
43
55
|
requirements:
|
|
44
56
|
- - "~>"
|
|
45
57
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '
|
|
58
|
+
version: '2.22'
|
|
47
59
|
type: :runtime
|
|
48
60
|
prerelease: false
|
|
49
61
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
62
|
requirements:
|
|
51
63
|
- - "~>"
|
|
52
64
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
65
|
+
version: '2.22'
|
|
54
66
|
- !ruby/object:Gem::Dependency
|
|
55
|
-
name:
|
|
67
|
+
name: discard
|
|
56
68
|
requirement: !ruby/object:Gem::Requirement
|
|
57
69
|
requirements:
|
|
58
70
|
- - "~>"
|
|
59
71
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
72
|
+
version: '1.3'
|
|
61
73
|
type: :runtime
|
|
62
74
|
prerelease: false
|
|
63
75
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
76
|
requirements:
|
|
65
77
|
- - "~>"
|
|
66
78
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '
|
|
79
|
+
version: '1.3'
|
|
68
80
|
- !ruby/object:Gem::Dependency
|
|
69
81
|
name: sidekiq
|
|
70
82
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -72,6 +84,9 @@ dependencies:
|
|
|
72
84
|
- - ">="
|
|
73
85
|
- !ruby/object:Gem::Version
|
|
74
86
|
version: '7.0'
|
|
87
|
+
- - "<"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '9.0'
|
|
75
90
|
type: :runtime
|
|
76
91
|
prerelease: false
|
|
77
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -79,6 +94,9 @@ dependencies:
|
|
|
79
94
|
- - ">="
|
|
80
95
|
- !ruby/object:Gem::Version
|
|
81
96
|
version: '7.0'
|
|
97
|
+
- - "<"
|
|
98
|
+
- !ruby/object:Gem::Version
|
|
99
|
+
version: '9.0'
|
|
82
100
|
- !ruby/object:Gem::Dependency
|
|
83
101
|
name: rake
|
|
84
102
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -160,6 +178,7 @@ extra_rdoc_files: []
|
|
|
160
178
|
files:
|
|
161
179
|
- CHANGELOG.md
|
|
162
180
|
- README.md
|
|
181
|
+
- SECURITY_AND_QUALITY_AUDIT.md
|
|
163
182
|
- lib/vert.rb
|
|
164
183
|
- lib/vert/authorization/controller_methods.rb
|
|
165
184
|
- lib/vert/authorization/dynamic_policy.rb
|
|
@@ -195,13 +214,14 @@ files:
|
|
|
195
214
|
- lib/vert/rls/context_middleware.rb
|
|
196
215
|
- lib/vert/rls/job_context.rb
|
|
197
216
|
- lib/vert/version.rb
|
|
217
|
+
- lib/vert_core.rb
|
|
198
218
|
- vert.gemspec
|
|
199
219
|
homepage: https://github.com/edinaldo-novti/vert
|
|
200
220
|
licenses:
|
|
201
221
|
- MIT
|
|
202
222
|
metadata:
|
|
203
223
|
homepage_uri: https://github.com/edinaldo-novti/vert
|
|
204
|
-
source_code_uri: https://github.com/edinaldo-novti/vert
|
|
224
|
+
source_code_uri: https://github.com/edinaldo-novti/vert/tree/main
|
|
205
225
|
changelog_uri: https://github.com/edinaldo-novti/vert/blob/main/CHANGELOG.md
|
|
206
226
|
rdoc_options: []
|
|
207
227
|
require_paths:
|