wizz-method 1.4.0 → 1.4.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.
- package/.claude-plugin/marketplace.json +4 -4
- package/README.md +1 -1
- package/evals/routing/dataset.json +290 -0
- package/evals/routing/run-routing-eval.mjs +89 -0
- package/package.json +11 -3
- package/skills-registry.yaml +65 -28
- package/src/modules/wizz/agents/wizz-ads/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-copy/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-designer/SKILL.md +6 -8
- package/src/modules/wizz/agents/wizz-growth/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-maestro/SKILL.md +6 -5
- package/src/modules/wizz/agents/wizz-memoria/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-qa/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-seo/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-social/SKILL.md +1 -1
- package/src/skills-lib/auth-and-secrets/SKILL.md +29 -11
- package/src/skills-lib/desktop-security/SKILL.md +35 -4
- package/src/skills-lib/premium-landing-ui-researcher/SKILL.md +52 -2083
- package/src/skills-lib/premium-landing-ui-researcher/references/audit-protocol.md +124 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/component-sources.md +336 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/core-goal.md +62 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/dashboard-and-portfolio-modes.md +92 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/handoffs.md +55 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/landing-page-strategy.md +227 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/mandatory-process.md +36 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/output-format-and-quality.md +129 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/prompt-templates.md +206 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/site-levels.md +515 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/source-first-protocol.md +144 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/source-links.md +39 -0
- package/src/skills-lib/premium-landing-ui-researcher/references/stack-and-visual-direction.md +118 -0
- package/src/skills-lib/taste-skill/SKILL.md +33 -1177
- package/src/skills-lib/taste-skill/references/anti-slop-tells.md +110 -0
- package/src/skills-lib/taste-skill/references/architecture-conventions.md +39 -0
- package/src/skills-lib/taste-skill/references/block-library.md +61 -0
- package/src/skills-lib/taste-skill/references/brief-and-dials.md +89 -0
- package/src/skills-lib/taste-skill/references/dark-mode.md +23 -0
- package/src/skills-lib/taste-skill/references/design-directives.md +191 -0
- package/src/skills-lib/taste-skill/references/design-systems.md +267 -0
- package/src/skills-lib/taste-skill/references/motion-patterns.md +167 -0
- package/src/skills-lib/taste-skill/references/pattern-vocabulary.md +78 -0
- package/src/skills-lib/taste-skill/references/performance-a11y.md +33 -0
- package/src/skills-lib/taste-skill/references/preflight-checklist.md +73 -0
- package/src/skills-lib/taste-skill/references/redesign-protocol.md +52 -0
- package/src/skills-lib/web-security/SKILL.md +27 -2
- package/src/skills-lib/wizz-router/SKILL.md +21 -246
- package/src/skills-lib/wizz-router/references/auditoria-360.md +21 -0
- package/src/skills-lib/wizz-router/references/routing-table-flat.md +95 -0
- package/src/squads/advisory-board/agents/ray-dalio.md +0 -20
- package/tools/hooks/README.md +17 -0
- package/tools/hooks/security-defensive-context.js +70 -0
- package/tools/hooks/session-rules.js +38 -0
- package/tools/hooks/wizz-router-enforce.js +188 -0
- package/tools/installer/modules/skills-lib.js +70 -1
- package/tools/sync-global.mjs +84 -0
- package/.claude/settings.local.json +0 -8
- package/src/core-skills/wizz-party-mode/scripts/__pycache__/resolve_party.cpython-314.pyc +0 -0
- package/src/skills-lib/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
- package/src/skills-lib/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
- package/src/skills-lib/ui-ux-pro-max/scripts/__pycache__/search.cpython-314.pyc +0 -0
- package/src/skills-lib/ui-ux-pro-max/scripts/graphify-out/cache/0964cef300f753b5cee0cc49777ca97035c85d45c212099405d9ee4da36e523f.json +0 -1
- package/src/skills-lib/ui-ux-pro-max/scripts/graphify-out/cache/21a40f6810c3a6f17acc3580f106171daf78c7b41bbd066bc80448a821ea0adf.json +0 -1
- package/src/skills-lib/ui-ux-pro-max/scripts/graphify-out/cache/69ea15a020ac81b8459e37ef63654398a50c1035fc01c43d8069df217389f7f3.json +0 -1
|
@@ -25,14 +25,12 @@ Você é o Designer do Wizz. Cria interfaces e landing pages de alto nível, mos
|
|
|
25
25
|
|
|
26
26
|
## Como trabalho (ponte para skills globais)
|
|
27
27
|
|
|
28
|
-
> **Fonte única (registry) — leia SEMPRE antes dos exemplos abaixo:** a lista real da sua área (`designer`) vive no `skills-registry.yaml` (
|
|
29
|
-
|
|
30
|
-
Para cada tarefa, **
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
- Olhar crítico anti-slop, qualidade de gosto visual → `taste-skill`
|
|
35
|
-
- Escolher componentes que combinam com o projeto → `ui-component-curator`
|
|
28
|
+
> **Fonte única (registry) — leia SEMPRE antes dos exemplos abaixo:** a lista real da sua área (`designer`) vive no `skills-registry.yaml`. Resolva primeiro a **fatia leve da sua área**, `{project-root}/_wizz/_config/registry/designer.yaml` (já vem como o bloco `areas.designer` completo — skills/mcps/clis/references); se faltar (install antigo), caia pro monólito na ordem `{project-root}/_wizz/_config/skills-registry.yaml` → `{project-root}/_wizz/skills-registry.yaml` → `{project-root}/skills-registry.yaml` e ache o bloco `areas.designer` lá dentro. Se precisar de algo cross-cutting (utility/mcp_utility/cli_utility/squads), leia também `{project-root}/_wizz/_config/registry/_shared.yaml`. Roteie pelo campo `when:` — `skills:` (invoque via `Skill`), `clis:` (rode o `check:`; se faltar, mostre o `install:`, opt-in; respeite `platform:` — ex. `buttercut` é só `darwin-arm64`) e `mcps:` (proponha `claude mcp add <id>` com o bloco `server`). **Portas de entrada (skills):** ofereça só as 3 skills com `entry: true` (direção → decision-maker, construção → ui-ux-pro-max, motion/assets → animate); as demais têm campo `door:` e só entram puxadas pela porta da camada delas ou por pedido explícito do usuário — nunca ofereça a lista inteira. Os exemplos abaixo são só um atalho legível; o registry é a verdade e pega o que for adicionado depois (ex. tools de vídeo: hyperframes, claude-video, buttercut, voicebox).
|
|
29
|
+
|
|
30
|
+
Para cada tarefa, **entre pela porta certa via a ferramenta `Skill`** e traga o resultado em linguagem fácil:
|
|
31
|
+
- Decidir ANTES de construir (brief, direção visual, caminho de motion/3D) → `decision-maker` (puxa taste-skill, motion-3d-director)
|
|
32
|
+
- Criar/melhorar UI (landing, design system, componente, polish) → `ui-ux-pro-max` (puxa premium-landing-ui-researcher, ui-component-curator, impeccable, taste-redesign, huashu-design, react-components)
|
|
33
|
+
- Executar animação e gerar mídia → `animate` (puxa design-motion-principles, remotion-best-practices, canvas-design, algorithmic-art)
|
|
36
34
|
|
|
37
35
|
Sempre **mostre o visual/plano antes do código**. Construção de código é com o **wizz-dev**.
|
|
38
36
|
|
|
@@ -20,7 +20,7 @@ Você é o Growth do Wizz. Traz ideias acionáveis de marketing e conversão, pl
|
|
|
20
20
|
|
|
21
21
|
## Como trabalho (ponte global)
|
|
22
22
|
|
|
23
|
-
> **Fonte única (registry) — leia SEMPRE antes dos exemplos abaixo:** a lista real da sua área (`growth`) vive no `skills-registry.yaml` (
|
|
23
|
+
> **Fonte única (registry) — leia SEMPRE antes dos exemplos abaixo:** a lista real da sua área (`growth`) vive no `skills-registry.yaml`. Resolva primeiro a **fatia leve da sua área**, `{project-root}/_wizz/_config/registry/growth.yaml` (já vem como o bloco `areas.growth` completo); se faltar (install antigo), caia pro monólito na ordem `{project-root}/_wizz/_config/skills-registry.yaml` → `{project-root}/_wizz/skills-registry.yaml` → `{project-root}/skills-registry.yaml` e ache o bloco `areas.growth` lá dentro. Precisando de algo cross-cutting (utility/mcp_utility/cli_utility/squads), leia `{project-root}/_wizz/_config/registry/_shared.yaml`. Ofereça **tudo que casar** com o pedido pelo `when:` — `skills:` (via `Skill`), `clis:` (`check:` → se faltar mostre o `install:`, opt-in, respeite `platform:`) e `mcps:` (`claude mcp add <id>` com o bloco `server`). Os exemplos abaixo são atalho legível; o registry é a verdade e pega novidades automático (ex. MCP `scrapling`).
|
|
24
24
|
- Ideias e estratégia de marketing → `marketing-ideas`
|
|
25
25
|
- Otimizar conversão de página / funil → `page-cro`
|
|
26
26
|
- Lançamento de produto/feature, go-to-market → `launch-strategy`
|
|
@@ -54,15 +54,16 @@ Execute cada item de `{agent.activation_steps_append}`.
|
|
|
54
54
|
|
|
55
55
|
### Passo 8 — Carregar o registry e rotear
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
O `skills-registry.yaml` é a fonte única (a mesma que o installer lê), mas você não precisa do monólito inteiro pra rotear. Carregue em camadas:
|
|
58
58
|
|
|
59
|
-
1. `{project-root}/_wizz/_config/
|
|
60
|
-
2. `{project-root}/_wizz/
|
|
61
|
-
3. `{project-root}/
|
|
59
|
+
1. **Índice leve primeiro:** `{project-root}/_wizz/_config/registry/index.yaml` — só `version` + `area → {agent, summary}`, o suficiente pra decidir qual área/agente casa com o pedido.
|
|
60
|
+
2. **Fatia(s) da(s) área(s) escolhida(s):** depois de saber a área, carregue só `{project-root}/_wizz/_config/registry/<area>.yaml` (ex. `designer.yaml`, `copy.yaml`) — vem como o bloco `areas.<area>` completo (skills/mcps/clis/references). Em pedido multi-área, carregue uma fatia por área envolvida, não o monólito.
|
|
61
|
+
3. **Cross-cutting sob demanda:** se o pedido precisar de `utility:`, `mcp_utility:`, `cli_utility:` ou `squads:`, carregue também `{project-root}/_wizz/_config/registry/_shared.yaml`.
|
|
62
|
+
4. **Fallback (install antigo sem fatias, ou fatia faltando):** caia pro monólito na ordem `{project-root}/_wizz/_config/skills-registry.yaml` → `{project-root}/_wizz/skills-registry.yaml` → `{project-root}/skills-registry.yaml` e ache o bloco lá dentro.
|
|
62
63
|
|
|
63
64
|
Se o usuário já disse a intenção, **classifique e despache direto** (veja Roteamento). Senão, faça 1 pergunta curta para descobrir a área e então despache.
|
|
64
65
|
|
|
65
|
-
> Fallback: se nenhum caminho existir, não invente a tabela. Faça a pergunta de área, siga com o melhor agente que você conhecer e avise que o registry não foi encontrado.
|
|
66
|
+
> Fallback final: se nenhum caminho existir (nem fatia, nem monólito), não invente a tabela. Faça a pergunta de área, siga com o melhor agente que você conhecer e avise que o registry não foi encontrado.
|
|
66
67
|
|
|
67
68
|
## Roteamento (você é o Gerente)
|
|
68
69
|
|
|
@@ -20,7 +20,7 @@ Você é a Memória do Wizz. Guarda e recupera o contexto do usuário entre sess
|
|
|
20
20
|
|
|
21
21
|
## Como trabalho (ponte global)
|
|
22
22
|
|
|
23
|
-
> **Fonte única (registry) — leia SEMPRE antes dos exemplos abaixo:** a lista real da sua área (`memoria`) vive no `skills-registry.yaml` (
|
|
23
|
+
> **Fonte única (registry) — leia SEMPRE antes dos exemplos abaixo:** a lista real da sua área (`memoria`) vive no `skills-registry.yaml`. Resolva primeiro a **fatia leve da sua área**, `{project-root}/_wizz/_config/registry/memoria.yaml` (já vem como o bloco `areas.memoria` completo); se faltar (install antigo), caia pro monólito na ordem `{project-root}/_wizz/_config/skills-registry.yaml` → `{project-root}/_wizz/skills-registry.yaml` → `{project-root}/skills-registry.yaml` e ache o bloco `areas.memoria` lá dentro. Precisando de algo cross-cutting (utility/mcp_utility/cli_utility/squads), leia `{project-root}/_wizz/_config/registry/_shared.yaml`. Ofereça **tudo que casar** com o pedido pelo `when:` — `skills:` (via `Skill`), `clis:` e `mcps:` (`claude mcp add <id>` com o bloco `server`). Os exemplos abaixo são atalho legível; o registry é a verdade e pega o que for adicionado depois.
|
|
24
24
|
- Ver estado atual do projeto → `cerebro` (`/ver`)
|
|
25
25
|
- Salvar a sessão → `cerebro` (`/salvar`)
|
|
26
26
|
- Registrar uma decisão de arquitetura/produto → `cerebro` (`/decisao`)
|
|
@@ -20,7 +20,7 @@ Você é o QA do Wizz. Entra **depois do wizz-dev**: pega o código pronto e ver
|
|
|
20
20
|
|
|
21
21
|
## Como trabalho (ponte global)
|
|
22
22
|
|
|
23
|
-
> **Fonte única (registry) — leia SEMPRE antes dos exemplos abaixo:** a lista real da sua área (`qa`) vive no `skills-registry.yaml` (
|
|
23
|
+
> **Fonte única (registry) — leia SEMPRE antes dos exemplos abaixo:** a lista real da sua área (`qa`) vive no `skills-registry.yaml`. Resolva primeiro a **fatia leve da sua área**, `{project-root}/_wizz/_config/registry/qa.yaml` (já vem como o bloco `areas.qa` completo); se faltar (install antigo), caia pro monólito na ordem `{project-root}/_wizz/_config/skills-registry.yaml` → `{project-root}/_wizz/skills-registry.yaml` → `{project-root}/skills-registry.yaml` e ache o bloco `areas.qa` lá dentro. Precisando de algo cross-cutting (utility/mcp_utility/cli_utility/squads), leia `{project-root}/_wizz/_config/registry/_shared.yaml`. Ofereça **tudo que casar** com o pedido pelo `when:` — `skills:` (via `Skill`) e `clis:` (`check:` → se faltar mostre o `install:`, opt-in, respeite `platform:`; ex. `agent-browser` p/ verificação de browser — nunca Playwright). Os exemplos abaixo são atalho legível; o registry é a verdade e pega o que for adicionado depois.
|
|
24
24
|
- Rodar a suíte de testes e reportar o que passou/falhou → executo os testes do projeto e resumo.
|
|
25
25
|
- Gerar testes E2E e rodar fluxos críticos → `e2e-runner` (ou `wizz-qa-generate-e2e-tests`).
|
|
26
26
|
- Revisão adversarial caçando bugs (assumir que tem bug) → `adversarial-reviewer`.
|
|
@@ -20,7 +20,7 @@ Você é o SEO do Wizz. Audita, prioriza e otimiza para Google e para buscas de
|
|
|
20
20
|
|
|
21
21
|
## Como trabalho (ponte global)
|
|
22
22
|
|
|
23
|
-
> **Fonte única (registry) — leia SEMPRE antes dos exemplos abaixo:** a lista real da sua área (`seo`) vive no `skills-registry.yaml` (
|
|
23
|
+
> **Fonte única (registry) — leia SEMPRE antes dos exemplos abaixo:** a lista real da sua área (`seo`) vive no `skills-registry.yaml`. Resolva primeiro a **fatia leve da sua área**, `{project-root}/_wizz/_config/registry/seo.yaml` (já vem como o bloco `areas.seo` completo); se faltar (install antigo), caia pro monólito na ordem `{project-root}/_wizz/_config/skills-registry.yaml` → `{project-root}/_wizz/skills-registry.yaml` → `{project-root}/skills-registry.yaml` e ache o bloco `areas.seo` lá dentro. Precisando de algo cross-cutting (utility/mcp_utility/cli_utility/squads), leia `{project-root}/_wizz/_config/registry/_shared.yaml`. Ofereça **tudo que casar** com o pedido pelo `when:` — `skills:` (via `Skill`), `clis:` (`check:` → se faltar mostre o `install:`, opt-in, respeite `platform:`) e `mcps:` (`claude mcp add <id>` com o bloco `server`). Os exemplos abaixo são atalho legível; o registry é a verdade e pega novidades automático (ex. `distribb`).
|
|
24
24
|
- Auditoria, por que não ranqueia, problemas técnicos → `seo-audit`
|
|
25
25
|
- Aparecer em ChatGPT/Perplexity/AI Overviews → `ai-seo`
|
|
26
26
|
- Dados estruturados / rich results → `schema-markup`
|
|
@@ -43,7 +43,7 @@ Opcionais (quando o tema pedir): **Novidade** (recém lançado? define Fórmula
|
|
|
43
43
|
|
|
44
44
|
## Como trabalho (ponte global)
|
|
45
45
|
|
|
46
|
-
> **Fonte única (registry) · leia SEMPRE antes dos exemplos abaixo:** a lista real da sua área (`social`) vive no `skills-registry.yaml` (
|
|
46
|
+
> **Fonte única (registry) · leia SEMPRE antes dos exemplos abaixo:** a lista real da sua área (`social`) vive no `skills-registry.yaml`. Resolva primeiro a **fatia leve da sua área**, `{project-root}/_wizz/_config/registry/social.yaml` (já vem como o bloco `areas.social` completo); se faltar (install antigo), caia pro monólito na ordem `{project-root}/_wizz/_config/skills-registry.yaml` → `{project-root}/_wizz/skills-registry.yaml` → `{project-root}/skills-registry.yaml` e ache o bloco `areas.social` lá dentro. Ofereça **tudo que casar** com o pedido pelo `when:`. `skills:` (via `Skill`), `clis:` (rode o `check:`; se faltar mostre o `install:`, opt-in; respeite `platform:`, ex. `buttercut` é só `darwin-arm64`) e `mcps:`. As CLIs de vídeo (hyperframes, claude-video, buttercut, voicebox) pertencem à área `designer`; leia `{project-root}/_wizz/_config/registry/designer.yaml` (ou `_shared.yaml` para utility/mcp_utility/cli_utility/squads cross-cutting) pra pegar o bloco delas. Os exemplos abaixo são atalho legível; o registry é a verdade e pega o que for adicionado depois.
|
|
47
47
|
|
|
48
48
|
Roteiro e conteúdo (você mesmo, com o blueprint + skills):
|
|
49
49
|
- Roteiro viral de Reel/Short, qualquer formato (hook, corpo, CTA) → aplique o blueprint (Seções 1-11; Seção 12 para o formato 3D-personagem).
|
|
@@ -13,18 +13,18 @@ description: >
|
|
|
13
13
|
|
|
14
14
|
### Senhas
|
|
15
15
|
- Hash com bcrypt (custo >= 12), Argon2id ou scrypt. Nunca MD5/SHA1 para senhas
|
|
16
|
-
-
|
|
16
|
+
- Mínimo de 8 caracteres, sem restrição de caracteres especiais
|
|
17
17
|
- Implemente bloqueio após N tentativas (lockout ou CAPTCHA)
|
|
18
|
-
-
|
|
18
|
+
- Ofereça 2FA: TOTP (Google Authenticator) é o padrão mínimo
|
|
19
19
|
|
|
20
20
|
### JWT
|
|
21
|
-
-
|
|
21
|
+
- Na verificação, aceite só o algoritmo esperado: rejeite `alg: none` e não deixe o token escolher o algoritmo (ataque de confusão de algoritmo)
|
|
22
|
+
- Prefira RS256 (assimétrico) quando mais de um serviço verifica o token. HS256 só com secret forte: 32+ bytes aleatórios, nunca uma palavra
|
|
22
23
|
- exp curto para access token (15 min a 1h)
|
|
23
24
|
- Refresh token com rotação: ao usar, invalide o anterior e emita novo
|
|
24
25
|
- Nunca coloque dados sensíveis no payload (é base64, não criptografia)
|
|
25
|
-
- Blacklist de tokens invalidados: Redis com TTL igual ao exp do token
|
|
26
26
|
- **Token no header `Authorization: Bearer`, NUNCA na URL/query string** (a URL vaza em logs de servidor, histórico do browser, header `Referer` e analytics)
|
|
27
|
-
- **Logout de verdade revoga o token**: coloque na blacklist (Redis, TTL = exp) e invalide o refresh token. Sem isso o access token continua válido depois do logout até expirar sozinho
|
|
27
|
+
- **Logout de verdade revoga o token**: coloque na blacklist (Redis, TTL = exp do token) e invalide o refresh token. Sem isso o access token continua válido depois do logout até expirar sozinho
|
|
28
28
|
|
|
29
29
|
### Enumeração de usuário
|
|
30
30
|
Evita que um atacante descubra quais e-mails existem na base (login, signup, reset de senha).
|
|
@@ -40,9 +40,10 @@ Evita que um atacante descubra quais e-mails existem na base (login, signup, res
|
|
|
40
40
|
|
|
41
41
|
### Stack Supabase + Clerk
|
|
42
42
|
- Auth gerenciada pelo Clerk: nunca reimplementar flows de auth manualmente
|
|
43
|
-
-
|
|
43
|
+
- Toda rota protegida, Server Action e route handler chama `auth()` do Clerk no servidor. Nunca confiar em estado de sessão vindo do cliente
|
|
44
|
+
- Clerk webhook (`svix`) verificado por assinatura antes de processar: não confiar no body sem verificar
|
|
44
45
|
- Supabase RLS: toda tabela de domínio deve ter RLS ativo; queries devem filtrar por `workspace_id`/`user_id`
|
|
45
|
-
- `SUPABASE_SERVICE_ROLE_KEY` só
|
|
46
|
+
- `SUPABASE_SERVICE_ROLE_KEY` bypassa RLS: só em server-side (API routes, jobs), nunca exposta no frontend
|
|
46
47
|
- `NEXT_PUBLIC_*` = seguro expor no cliente; tudo sem `NEXT_PUBLIC_` = server-only
|
|
47
48
|
|
|
48
49
|
## Secrets e credenciais
|
|
@@ -56,19 +57,36 @@ Evita que um atacante descubra quais e-mails existem na base (login, signup, res
|
|
|
56
57
|
### Onde guardar secrets
|
|
57
58
|
- Produção: variáveis de ambiente da plataforma (Vercel: `vercel env add`)
|
|
58
59
|
- CI/CD: variáveis de ambiente criptografadas da plataforma (GitHub Actions Secrets, etc.)
|
|
59
|
-
- Dev local: arquivo `.env.local` nunca commitado
|
|
60
|
+
- Dev local: arquivo `.env.local` nunca commitado. Verificar com `git check-ignore -v .env.local`
|
|
61
|
+
|
|
62
|
+
### API keys emitidas pelo próprio app
|
|
63
|
+
Se o app gera API keys para os usuários:
|
|
64
|
+
- Guarde só o hash (SHA-256) da key no banco, nunca a key em texto plano
|
|
65
|
+
- Mostre a key completa uma única vez, na criação
|
|
66
|
+
- Use prefixo identificável (ex: `wz_live_`) para facilitar detecção em scan de repositório
|
|
67
|
+
- Permita revogar e listar keys por usuário
|
|
60
68
|
|
|
61
69
|
### Cripto de credenciais de usuário (AES-256-GCM)
|
|
62
70
|
Se o app armazena credenciais de terceiros do usuário (ex: SMTP, WhatsApp API):
|
|
63
71
|
```ts
|
|
64
|
-
// src/lib/crypto.ts
|
|
65
|
-
|
|
72
|
+
// src/lib/crypto.ts: chave vem do env, nunca hardcodada
|
|
73
|
+
import { createCipheriv, randomBytes } from "crypto"
|
|
74
|
+
|
|
75
|
+
const key = process.env.CREDENTIAL_ENCRYPTION_KEY // base64, 32 bytes
|
|
66
76
|
if (!key) throw new Error("CREDENTIAL_ENCRYPTION_KEY não configurada")
|
|
67
|
-
|
|
77
|
+
|
|
78
|
+
export function encrypt(plain: string) {
|
|
79
|
+
const iv = randomBytes(12) // IV aleatório por registro, nunca reutilizar
|
|
80
|
+
const cipher = createCipheriv("aes-256-gcm", Buffer.from(key, "base64"), iv)
|
|
81
|
+
const enc = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()])
|
|
82
|
+
return { iv: iv.toString("base64"), data: enc.toString("base64"), tag: cipher.getAuthTag().toString("base64") }
|
|
83
|
+
}
|
|
84
|
+
// no decrypt: setAuthTag antes de final(), senão a integridade não é verificada
|
|
68
85
|
```
|
|
69
86
|
|
|
70
87
|
### Rotação de credenciais
|
|
71
88
|
- Rotacione secrets de terceiros a cada 90 dias ou após qualquer saída de membro do time
|
|
89
|
+
- Secret commitado por acidente: considere comprometido e rotacione na hora. Reescrever o histórico do git não basta
|
|
72
90
|
- API keys de produção: uma por serviço/ambiente, nunca compartilhadas
|
|
73
91
|
- Database passwords: use IAM auth quando disponível no cloud provider
|
|
74
92
|
|
|
@@ -15,11 +15,32 @@ description: >
|
|
|
15
15
|
- contextIsolation: true (obrigatório)
|
|
16
16
|
- sandbox: true quando possível
|
|
17
17
|
- webSecurity: false nunca em produção
|
|
18
|
+
- Nunca carregue conteúdo remoto numa janela com acesso a APIs privilegiadas; conteúdo remoto = sandbox + sem preload sensível
|
|
19
|
+
- Mantenha o Electron atualizado (patches de Chromium/Node chegam por release do Electron)
|
|
20
|
+
|
|
21
|
+
### Navegação e janelas
|
|
22
|
+
- Bloqueie navegação para fora do app: handler em `will-navigate` que cancela URLs fora da allowlist
|
|
23
|
+
- `setWindowOpenHandler`: negue por padrão (`{ action: "deny" }`), abra externo só o que for aprovado
|
|
24
|
+
- `shell.openExternal` só com URL validada (esquema `https:` e host esperado), nunca com input cru do renderer
|
|
18
25
|
|
|
19
26
|
### IPC (comunicação renderer <> main)
|
|
20
|
-
- Valide e sanitize todos os dados vindos do renderer
|
|
21
|
-
- Exponha apenas as funções necessárias via contextBridge
|
|
27
|
+
- Valide e sanitize todos os dados vindos do renderer (o renderer é território hostil: trate como input de usuário)
|
|
28
|
+
- Exponha apenas as funções necessárias via contextBridge, cada uma com assinatura fixa
|
|
22
29
|
- Nunca exponha ipcRenderer diretamente ao renderer
|
|
30
|
+
- Em `ipcMain.handle`, valide tipo e tamanho dos argumentos; se houver múltiplas janelas, cheque `event.senderFrame.url` contra o esperado
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
// preload.ts: superfície mínima, sem repassar ipcRenderer
|
|
34
|
+
contextBridge.exposeInMainWorld("api", {
|
|
35
|
+
saveNote: (text: string) => ipcRenderer.invoke("notes:save", text),
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// main.ts: valida antes de agir
|
|
39
|
+
ipcMain.handle("notes:save", (event, text) => {
|
|
40
|
+
if (typeof text !== "string" || text.length > 10_000) throw new Error("input inválido")
|
|
41
|
+
// ...
|
|
42
|
+
})
|
|
43
|
+
```
|
|
23
44
|
|
|
24
45
|
### Atualizações automáticas
|
|
25
46
|
- Assine o pacote de atualização com certificado de código (code signing)
|
|
@@ -29,8 +50,9 @@ description: >
|
|
|
29
50
|
## Armazenamento local
|
|
30
51
|
|
|
31
52
|
### O que armazenar localmente
|
|
32
|
-
- Tokens de sessão:
|
|
33
|
-
-
|
|
53
|
+
- Tokens de sessão: `safeStorage` do Electron (usa Keychain no macOS, DPAPI no Windows, libsecret no Linux), nunca localStorage
|
|
54
|
+
- `safeStorage.encryptString(token)` antes de gravar em disco; `keytar` está arquivado, não use em projeto novo
|
|
55
|
+
- Dados sensíveis do usuário: criptografados via `safeStorage` ou AES-256-GCM com chave guardada no keychain do OS
|
|
34
56
|
|
|
35
57
|
### O que nunca armazenar localmente
|
|
36
58
|
- Senhas em texto plano
|
|
@@ -41,3 +63,12 @@ description: >
|
|
|
41
63
|
- Code signing obrigatório (Windows: Authenticode, macOS: Apple Developer ID)
|
|
42
64
|
- Notarização no macOS para distribuição fora da App Store
|
|
43
65
|
- Auto-update com verificação de integridade antes de executar
|
|
66
|
+
|
|
67
|
+
## Checklist rápido (antes de empacotar)
|
|
68
|
+
- [ ] `contextIsolation: true`, `nodeIntegration: false`, `sandbox: true` onde possível?
|
|
69
|
+
- [ ] Nenhuma janela privilegiada carrega URL remota?
|
|
70
|
+
- [ ] `will-navigate` e `setWindowOpenHandler` restringem navegação?
|
|
71
|
+
- [ ] Todo handler de `ipcMain` valida os argumentos?
|
|
72
|
+
- [ ] Tokens em `safeStorage`/keychain, nada sensível em localStorage?
|
|
73
|
+
- [ ] Build assinado (e notarizado no macOS)? Auto-update valida assinatura?
|
|
74
|
+
- [ ] Versão do Electron atual (sem CVE aberta)?
|