enigma-cli 1.33.2 → 1.33.3
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/assets/skills/anti-overengineering-policy/skill.json +1 -1
- package/assets/skills/anti-overengineering-review/skill.json +1 -1
- package/assets/skills/backend-policy/skill.json +1 -1
- package/assets/skills/ciphera-style-policy/skill.json +1 -1
- package/assets/skills/code-review-policy/skill.json +1 -1
- package/assets/skills/core-engineering-policy/skill.json +1 -1
- package/assets/skills/database-expert/skill.json +1 -1
- package/assets/skills/debugging-policy/skill.json +1 -1
- package/assets/skills/dependency-policy/skill.json +1 -1
- package/assets/skills/email-policy/skill.json +1 -1
- package/assets/skills/frontend-design/skill.json +1 -1
- package/assets/skills/frontend-policy/SKILL.md +33 -1
- package/assets/skills/frontend-policy/skill.json +3 -3
- package/assets/skills/git-policy/skill.json +1 -1
- package/assets/skills/logo-sourcing-policy/skill.json +1 -1
- package/assets/skills/security-policy/SKILL.md +13 -2
- package/assets/skills/security-policy/skill.json +3 -3
- package/assets/skills/skill-creator/skill.json +1 -1
- package/assets/skills/task-completion-policy/skill.json +1 -1
- package/assets/skills/technical-writing-policy/skill.json +1 -1
- package/assets/skills/testing-policy/skill.json +1 -1
- package/assets/skills/validation-policy/SKILL.md +10 -1
- package/assets/skills/validation-policy/skill.json +3 -3
- package/bin/checksums.json +4 -4
- package/dist/guardrails.js +51 -0
- package/package.json +1 -1
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "On-demand over-engineering review - diff review, whole-repo audit, and enigma: debt-marker ledger (tags delete/stdlib/native/yagni/shrink, line/dep scoring); lists cuts, applies nothing.",
|
|
6
6
|
"updated": "2026-06-16T11:24:30+02:00",
|
|
7
|
-
"cliVersion": "1.33.
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
8
|
"sha": "f742a2be3f328b9ea1ff9a35a449177c2cbec35ad16e46f7054b7a873a2ab017"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Backend/API architecture: controller-service-repository layering, API and request optimization (batching, avoiding redundant calls, skipping no-op writes), server-side caching (Redis), and Zod boundary validation.",
|
|
6
6
|
"updated": "2026-08-01T15:51:38+02:00",
|
|
7
|
-
"cliVersion": "1.33.
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
8
|
"sha": "572fcf9780e3892942c300214367d7205ed6dcde173a64c61c1e519506cf0cfa"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Ciphera code style conventions (formatting, naming, imports incl. namespace imports for wide module surfaces, comments, code-level anti-patterns; TypeScript-first, language-agnostic).",
|
|
6
6
|
"updated": "2026-07-31T21:32:28+02:00",
|
|
7
|
-
"cliVersion": "1.33.
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
8
|
"sha": "d6a86c28ad39ee502607a5ca8f6f361c8aa52b8fa946156165ec78c6139e495f"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Pre-delivery self-review gate, prioritized review dimensions, and change-quality criteria.",
|
|
6
6
|
"updated": "2026-06-01T00:45:28+02:00",
|
|
7
|
-
"cliVersion": "1.33.
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
8
|
"sha": "3d3bbe0602d5bbb4afe37648fe3c2fa39376b1bcbac5d8c441f01fad1e866ed0"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Core engineering execution policy and harness orchestration (highest-authority rules).",
|
|
6
6
|
"updated": "2026-07-30T19:29:19+02:00",
|
|
7
|
-
"cliVersion": "1.33.
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
8
|
"sha": "6a881d8589926fa7f48058314fd26d7042fd2ac82f1c87a6c11ffb54d1fda22b"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Senior database architecture policy: engine selection (PostgreSQL by default, SQLite only for local-first/embedded stores), ORM selection (Prisma in TypeScript/JavaScript), query optimization, anti-duplication/normalization, scalability, and RGPD/GDPR encryption.",
|
|
6
6
|
"updated": "2026-08-01T15:51:38+02:00",
|
|
7
|
-
"cliVersion": "1.33.
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
8
|
"sha": "4b7569446786c5bdbb71b2e9102aee0d8dd391b5e3e461d6fb1f05294d446ad9"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Reproduce-isolate-fix debugging methodology with root-cause discipline and regression verification.",
|
|
6
6
|
"updated": "2026-06-01T00:45:28+02:00",
|
|
7
|
-
"cliVersion": "1.33.
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
8
|
"sha": "14b0064c8b33a0dc85e51464b05005cf5801c756b1101789a6924b9548420f6b"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Dependency and supply-chain security: lockfiles and reproducible installs, version pinning, vulnerability auditing, vetting/minimizing packages, vendoring, and SBOM/provenance.",
|
|
6
6
|
"updated": "2026-06-01T00:45:28+02:00",
|
|
7
|
-
"cliVersion": "1.33.
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
8
|
"sha": "6375d835c2aef2c9bd31ce116444dc3d796f510f9970a213aa3ac4696d7e21b9"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Transactional email: React Email templates instead of hand-written HTML tables, server-side rendering, one send module behind the provider SDK, plain-text alternatives, idempotent background sending, link safety, and deliverability (SPF/DKIM/DMARC, bounce suppression, unsubscribe).",
|
|
6
6
|
"updated": "2026-07-30T19:29:19+02:00",
|
|
7
|
-
"cliVersion": "1.33.
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
8
|
"sha": "c9724fdbcdbeab99573be3fd44d4cdd97c2a394d99f3c4395f118f17356b00ed"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one.",
|
|
6
6
|
"updated": "2026-07-29T01:18:36+02:00",
|
|
7
|
-
"cliVersion": "1.33.
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
8
|
"sha": "9e30ee7d8a1a1e8c6e7f4e043857cd01841c68a427752e45bc0cad9ec5cfa279"
|
|
9
9
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: frontend-policy
|
|
3
|
-
description: Frontend architecture - reusable components, abstraction thresholds, state management, no-op detection (skip any operation whose result equals the current state - form saves, toggles, filters, reorders - not just saves; dirty means the values DIFFER from the loaded snapshot, not that the user touched the field, so a value edited and put back leaves Save disabled), client-side caching (localStorage/sessionStorage to avoid redundant server calls and survive rate limits), instant first paint (render the shell immediately, load data async via the API, show skeletons - never block render on data), perceived performance and responsiveness (instant interaction feedback, prefetch on intent, debounce/throttle, cancel stale requests, avoid request waterfalls, lazy-load heavy widgets), large-list rendering (virtualized infinite scroll vs pagination, skeletons, progressive/parallel loading, short-TTL caching), optimistic UI with rollback, visual restraint (never a card inside a card, borders only where they carry information, spacing and background tone before chrome), icon actions (repeated row/card actions like copy, edit, rename, remove, download, refresh are icon-only buttons carrying aria-label plus title, never a text label), responsive/adaptive layout (fluid units, breakpoints, no overlap or horizontal overflow, viewport meta, touch targets), form fields that declare their keyboard and casing (autocapitalize/autocomplete/inputmode/spellcheck per field kind, set once in the shared Input, normalized on blur rather than on every keystroke, with an inline error on every field that has a rule), auth screens (breached-password feedback, strength meter, cookie consent answered before login/register), AI chat/assistant/agent interfaces (use Vercel's AI Elements registry for message threads, streaming, reasoning and tool-call panels, prompt inputs - never hand-roll chat UI in React), and periodic React code-health audits (react-doctor). Use when building or changing UI components, client state, forms/save flows, data fetching/caching, lists that show lots of data, loading states, dashboards/panels, layout/responsiveness, making the UI feel fast, building a chat/AI/agent/LLM interface, or any frontend structure.
|
|
3
|
+
description: Frontend architecture - reusable components, abstraction thresholds, state management, no-op detection (skip any operation whose result equals the current state - form saves, toggles, filters, reorders - not just saves; dirty means the values DIFFER from the loaded snapshot, not that the user touched the field, so a value edited and put back leaves Save disabled), client-side caching (localStorage/sessionStorage to avoid redundant server calls and survive rate limits), instant first paint (render the shell immediately, load data async via the API, show skeletons - never block render on data), perceived performance and responsiveness (instant interaction feedback, prefetch on intent, debounce/throttle, cancel stale requests, avoid request waterfalls, lazy-load heavy widgets), large-list rendering (virtualized infinite scroll vs pagination, skeletons, progressive/parallel loading, short-TTL caching), optimistic UI with rollback, visual restraint (never a card inside a card, borders only where they carry information, spacing and background tone before chrome), icon actions (repeated row/card actions like copy, edit, rename, remove, download, refresh are icon-only buttons carrying aria-label plus title, never a text label), navigation that is iconified and grouped into labelled sections once it outgrows a flat list, a Cmd/Ctrl+K command palette with fuse.js fuzzy search over the loaded data once the app has enough destinations and records to hunt through, responsive/adaptive layout (fluid units, breakpoints, no overlap or horizontal overflow, viewport meta, touch targets), form fields that declare their keyboard and casing (autocapitalize/autocomplete/inputmode/spellcheck per field kind, set once in the shared Input, normalized on blur rather than on every keystroke, with an inline error on every field that has a rule), auth screens (breached-password feedback, strength meter, cookie consent answered before login/register), AI chat/assistant/agent interfaces (use Vercel's AI Elements registry for message threads, streaming, reasoning and tool-call panels, prompt inputs - never hand-roll chat UI in React), and periodic React code-health audits (react-doctor). Use when building or changing UI components, client state, forms/save flows, data fetching/caching, lists that show lots of data, loading states, dashboards/panels, layout/responsiveness, making the UI feel fast, building a chat/AI/agent/LLM interface, or any frontend structure.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Frontend Architecture Policy
|
|
@@ -146,6 +146,24 @@ A sidebar keeps its desktop width only while there is a desktop to put it in. On
|
|
|
146
146
|
|
|
147
147
|
---
|
|
148
148
|
|
|
149
|
+
## Navigation Is Structured, Not A Growing List
|
|
150
|
+
|
|
151
|
+
A sidebar gets one more entry per feature and nobody ever goes back to reorganize it, so it ends as a flat column of a dozen similar words that has to be read top to bottom every time. Structure it as it grows, by default and without being asked: the icons, the grouping and the ordering below are part of building the nav, not a redesign to propose afterwards.
|
|
152
|
+
|
|
153
|
+
**Every entry carries an icon.** One icon set for the whole nav, one size, aligned in a single column so the labels line up. The glyph is what the eye aims at once the user knows the app, and it is what makes a collapsed rail possible later. Choose it for what the destination IS, not for the word in the label: a gauge for a dashboard, a key for credentials, sliders for settings. When no glyph reads for an abstract destination, take the set's neutral placeholder rather than a loose near-match - a wrong icon is read as a different feature.
|
|
154
|
+
|
|
155
|
+
**The label stays.** This is the opposite call from Icon Actions above, and the difference is repetition: a row action repeats per row and its glyph is conventional, while a nav entry appears once and names a place the user may never have visited. Icon plus word, and the icon takes `aria-hidden="true"` because the label is already the accessible name.
|
|
156
|
+
|
|
157
|
+
**Group once the list passes about seven entries.** Labelled sections of related destinations, with the label a quiet uppercase or muted heading rather than another bordered box (Visual Hierarchy above). Order the groups and the entries inside them by how often they are used, never alphabetically - alphabetical order is the one arrangement that guarantees the daily destination is somewhere in the middle. Account, billing and sign out sit apart at the bottom, separated by space.
|
|
158
|
+
|
|
159
|
+
**Mark the active entry with more than colour**: a filled background or a leading bar plus `aria-current="page"`, so it survives a colourblind user and a screen reader. A group holding the active entry stays expanded.
|
|
160
|
+
|
|
161
|
+
**Collapsible groups persist their state.** If a section can be collapsed, remember the choice per user (localStorage is enough) and restore it on the next visit; a nav that reopens every group on every navigation is worse than one that never collapsed.
|
|
162
|
+
|
|
163
|
+
**Nav is not the only way in.** Past roughly a dozen destinations, grouping stops being enough and the user starts hunting: add the command palette from Search & Filtering below. Structure narrows the hunt; search ends it.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
149
167
|
## Links In Copy Are Links
|
|
150
168
|
|
|
151
169
|
When UI copy names a destination - a URL, a doc page, a dashboard, a settings screen, an external service - make it reachable from where it is written. Printing a bare URL as plain text in a hint, description, empty state or error message leaves the user to select and copy it by hand, which is exactly the work the interface exists to remove.
|
|
@@ -262,6 +280,7 @@ Auth is the first screen a user meets and the one most often shipped half-built.
|
|
|
262
280
|
- The reset request screen confirms in the same words whether or not the address is registered ("If that address has an account, we have sent a link"). Never render "no account with that email" - the screen would be an account-existence oracle.
|
|
263
281
|
- The new-password screen validates in real time against the same schema the server uses, uses the shared Input (which brings the show/hide toggle), and compares the confirmation field as the user types. Keep Submit disabled until both are valid, with the reason visible.
|
|
264
282
|
- Wherever a password is created (sign-up, reset, change), check it against Have I Been Pwned's Pwned Passwords range API as the user types - debounced, request aborted when the value changes - and refuse a breached one with "This password appeared in a data breach. Choose a different one." Free, no key, and the password never leaves the browser: only the first 5 characters of its SHA-1 do (mechanics and the server-side half in security-policy).
|
|
283
|
+
- The same screens refuse a password built out of the identity it protects: the email, its local part, the username, the display name, the site name. Compare normalized values (lowercased, accent-stripped, punctuation dropped) so `F.J.R.G_2007` and `fjrg2007` are one string, and check containment and near-matches, not just equality (the rule and the server half are security-policy's). The form already holds the email and the name the user just typed, so run it on every keystroke and name the reason inline: "Your password cannot contain your email address or username."
|
|
265
284
|
- Next to it, a strength meter: a row of segments that fills and shifts colour (red, amber, green) with a one-line verdict. Drive it from an entropy estimator (`@zxcvbn-ts/core`), fed the user's own email and name as context, not from a regex counting character classes - "Passw0rd!" satisfies every class rule and is guessed instantly. The meter is advisory; the length floor and the breach check are the gate.
|
|
266
285
|
- The consent banner is answered before either form submits (security-policy owns the cookie rules). Blocking the account on a "reject" is not the ask: record the choice, then continue.
|
|
267
286
|
- After sign-up the user lands inside the app, already signed in. If the account still needs email verification, say so in the app with a way to resend, and block only the actions that need it.
|
|
@@ -429,6 +448,19 @@ For a user-facing search box or finder over a list, use fuse.js (fuzzy search) r
|
|
|
429
448
|
- Reach for fuse.js whenever the input is a search/filter box the user types free text into. Keep a plain equality/predicate filter only for exact, structured filtering (a status dropdown, a tag toggle) where fuzziness would be wrong.
|
|
430
449
|
- Configure the searched `keys` and a sensible `threshold`, and run the search over the already-loaded client list where possible (reuse the data, per Client-Side Caching) before falling back to a server query.
|
|
431
450
|
|
|
451
|
+
### Ctrl+K Opens A Command Palette Once There Is Enough To Hunt For
|
|
452
|
+
|
|
453
|
+
When an app has more destinations and records than fit comfortably in the nav - roughly a dozen sidebar entries, or any list the user scrolls to find a known item - the fastest path stops being pointing and becomes typing. Ship the palette then, by default and without being asked. Before that, a three-screen app does not need one (anti-overengineering-policy).
|
|
454
|
+
|
|
455
|
+
- **The shortcut is Cmd+K on macOS and Ctrl+K elsewhere**, bound once on the document and `preventDefault`ed (Ctrl+K focuses the browser's own search bar otherwise). The same handler answers `/` where the app has no text input focused, and Escape always closes.
|
|
456
|
+
- **It must also be clickable.** A shortcut nobody discovers is not an entry point: put the search field in the header or at the top of the sidebar showing the shortcut hint inside it ("Search Ctrl K"), and open the palette when it is clicked. Detect the platform for the hint rather than printing both.
|
|
457
|
+
- **It opens centred over the page as a modal dialog**, near the top third so the results grow downwards without moving the input. It is a real dialog: `role="dialog"` with `aria-modal`, focus into the field, focus trapped while open, focus restored to the trigger on close, page scroll locked. Reuse the project's Modal - the palette is a variant of it, not a second dialog implementation (Component Reuse above).
|
|
458
|
+
- **Search the data the client already holds**, fuzzy-matched with fuse.js in TS/JS, re-run on every keystroke - the in-memory pass is cheap and debouncing it only adds lag. Debounce only the part that reaches the server (~200-300ms) and abort the in-flight request when the query changes.
|
|
459
|
+
- **Index everything the user thinks of as a thing**, not just pages: navigation destinations, records (projects, accounts, files), and actions ("Create project", "Sign out", "Toggle theme"). Group the results by kind with a heading per group, and show the destination path or a second line so two similarly named records are distinguishable.
|
|
460
|
+
- **Empty query is not an empty panel.** Show recent items and the handful of most likely destinations, so opening it is useful before anything is typed.
|
|
461
|
+
- **Full keyboard contract**: Up/Down move through results across group boundaries, Enter opens the highlighted one, Cmd/Ctrl+Enter opens it in a new tab where that makes sense, and the highlighted row is tracked with `aria-activedescendant` on a `role="listbox"`. The mouse hovering a row moves the highlight so the two never disagree.
|
|
462
|
+
- **A no-results state names the query** and offers the nearest useful action ("No match for 'billling' - search all records"), rather than rendering nothing.
|
|
463
|
+
|
|
432
464
|
---
|
|
433
465
|
|
|
434
466
|
## AI Chat & Agent Interfaces
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"version": "1.23.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Frontend architecture: reusable components, abstraction thresholds, state management, no-op detection (skip any operation whose result equals current state, not just form saves; dirty means different from the loaded snapshot, not touched), instant first paint (render the shell, load data async, skeletons), perceived performance (prefetch on intent, debounce/throttle, cancel stale requests, avoid waterfalls, lazy widgets), large-list rendering (infinite scroll/pagination, virtualization, skeletons, progressive loading), optimistic UI with rollback, visual restraint (one card level, spacing before borders, one elevation scale), icon actions (repeated row/card actions are icon-only buttons with aria-label plus title, not text labels), responsive/adaptive layout (fluid units, breakpoints, no overlap/overflow, viewport meta, touch targets), form fields that declare their keyboard and casing (autocapitalize/autocomplete/inputmode per field kind, set once in the shared Input, normalized on blur), variable-length text (min-width:0 in flex/grid, wrap vs truncate, long unbroken strings, worst-case content checks), auth screens (breached-password feedback, strength meter, cookie consent before login/register), and AI chat/agent interfaces via Vercel's AI Elements registry instead of hand-rolled message threads.",
|
|
6
|
-
"updated": "2026-08-
|
|
7
|
-
"cliVersion": "1.33.
|
|
8
|
-
"sha": "
|
|
6
|
+
"updated": "2026-08-01T17:44:16+02:00",
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
|
+
"sha": "434b0bb9dd8e19d68dc823198df64feb873e3644ae483bcd14b75d9e3d0f74ef"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Git & contribution policy (senior engineering standards).",
|
|
6
6
|
"updated": "2026-07-16T22:44:02+02:00",
|
|
7
|
-
"cliVersion": "1.33.
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
8
|
"sha": "e6dfbc33884000d9d25841bd9c5a84d6558ffd374882cb7b34451eb2cebc2161"
|
|
9
9
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: security-policy
|
|
3
|
-
description: Application and AI-agent security - secrets management, authentication and authorization (least privilege), credential flows (sign-in, sign-up that establishes the session, password reset, 2FA, breached-password checks against Have I Been Pwned, and rate limiting per IP and per account), cookie attributes and consent gating before non-essential storage, OWASP Top 10 mitigations, transport and crypto baseline, secure logging, and agent/MCP/tool-use safety (prompt injection, untrusted tool output, permission boundaries). Use when handling secrets, auth, login or registration screens, permissions, untrusted data or tool output, or any security-sensitive code, config, or infrastructure.
|
|
3
|
+
description: Application and AI-agent security - secrets management, authentication and authorization (least privilege), credential flows (sign-in, sign-up that establishes the session, password reset, 2FA, breached-password checks against Have I Been Pwned, refusing a password that repeats the username, email or display name in any casing, and rate limiting per IP and per account), cookie attributes and consent gating before non-essential storage, OWASP Top 10 mitigations, transport and crypto baseline, secure logging, and agent/MCP/tool-use safety (prompt injection, untrusted tool output, permission boundaries). Use when handling secrets, auth, login or registration screens, permissions, untrusted data or tool output, or any security-sensitive code, config, or infrastructure.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Security Policy
|
|
@@ -63,7 +63,18 @@ These four screens are one system: an attacker who cannot guess a password will
|
|
|
63
63
|
- Run it in real time while the user types (debounce ~400ms, abort the in-flight request when the value changes) so the answer is on screen before Submit, and run it again server-side on submit. The client check is UX; the server check is the rule.
|
|
64
64
|
- Fail OPEN. If the lookup errors or times out, accept the password and log it: blocking every registration on a third-party outage is the worse failure.
|
|
65
65
|
- Say what happened and what to do - "This password appeared in a data breach. Choose a different one." The breach count is optional; blaming the user is not.
|
|
66
|
-
- Do not stack composition rules on top (one symbol, one digit, forced rotation). NIST SP 800-63B dropped them: a length floor (12+), the breach check and rate limiting are the controls that work.
|
|
66
|
+
- Do not stack composition rules on top (one symbol, one digit, forced rotation). NIST SP 800-63B dropped them: a length floor (12+), the breach check, the identity check below and rate limiting are the controls that work.
|
|
67
|
+
|
|
68
|
+
### A password may not be the account's own identity
|
|
69
|
+
|
|
70
|
+
The same screens that check the breach corpus reject a password built out of the identity it protects. `Fjrg2007` for the user `fjrg2007` is one guess for anyone holding the email address, and it is the first thing a targeted attacker tries. NIST SP 800-63B names context-specific words - the username, the service name, the address - as the other list to refuse, next to the breach corpus.
|
|
71
|
+
|
|
72
|
+
- Compare against every identifier the account is known by: the email, the email's local part, the username or handle, the display name, and the site or company name. Check each one separately; a password equal to the local part passes a check that only compared the full address.
|
|
73
|
+
- **Compare NORMALIZED values, never raw ones.** Lowercase both sides, trim, normalize Unicode to NFKD and strip the accents, then drop everything that is not a letter or a digit. `F.J.R.G_2007`, `FJRG2007` and `fjrg 2007` all reduce to `fjrg2007`, which is the point: case, punctuation and spacing are not differences an attacker has to guess. Reuse the shared normalizer that already canonicalizes the email and the handle (validation-policy).
|
|
74
|
+
- Reject on three relations, not just equality: the normalized password EQUALS an identifier, CONTAINS one that is 4 characters or longer (`myfjrg2007pass` still hands over the pattern), or is a near-match. For the near-match use a similarity ratio (Django's `UserAttributeSimilarityValidator` uses `SequenceMatcher` at 0.7); a padded year or a leetspeak swap (`fjrg2007!`, `fjrg20o7`) is the case that catches.
|
|
75
|
+
- **The server is the authority.** It holds the real identity, so it runs the comparison on every password-creating request even when the client already did. On sign-up the values come from the submitted form; on reset and change they come from the account being modified, never from the request body.
|
|
76
|
+
- A strength meter fed the user's own email and name (`@zxcvbn-ts/core` with `userInputs`) will score this password badly, and that is not the same thing: the meter is advisory and a score threshold is not a gate. Keep the equality, containment and similarity check as its own rule with its own refusal.
|
|
77
|
+
- Say which rule refused and what to change: "Your password cannot contain your email address or username." At creation time the user already knows their own identifiers, so this reveals nothing an attacker could use, unlike a sign-in error.
|
|
67
78
|
|
|
68
79
|
### Rate-limit by IP AND by account
|
|
69
80
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"version": "1.2.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Application and AI-agent security: secrets, authn/authz (least privilege), credential flows with breach-checked passwords (Have I Been Pwned), OWASP Top 10, transport/crypto baseline, cookies and consent, secure logging, and agent/MCP/tool-use safety.",
|
|
6
|
-
"updated": "2026-08-
|
|
7
|
-
"cliVersion": "1.33.
|
|
8
|
-
"sha": "
|
|
6
|
+
"updated": "2026-08-01T17:44:16+02:00",
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
|
+
"sha": "c9eac7999b326e4564816b10f3f681b9f6b2a88595d56244909757fb4bbc3c7f"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Create new skills, modify and improve existing skills, and measure skill performance with evals and benchmarks.",
|
|
6
6
|
"updated": "2026-07-29T01:18:36+02:00",
|
|
7
|
-
"cliVersion": "1.33.
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
8
|
"sha": "13d24c217bdb6fe83fe16835d8f5c3d397a9f3338f16876c61ef96e97f34c90a"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Exhaustive completion discipline for long/multi-item tasks - inventory, coverage ledger, verified done.",
|
|
6
6
|
"updated": "2026-07-30T20:10:24+02:00",
|
|
7
|
-
"cliVersion": "1.33.
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
8
|
"sha": "2f82288d83dca3676d6bc49955c89477cc298f77fa56b4e414cd95ff858fa261"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Concise, realistic technical copy - UI microcopy, descriptions, hints, empty/error states, and README/doc prose that informs without over-explaining or restating the obvious, and never uses a typographic dash.",
|
|
6
6
|
"updated": "2026-07-28T20:30:23+02:00",
|
|
7
|
-
"cliVersion": "1.33.
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
8
|
"sha": "a4b792103eb1f9dad93b9d70ea79dc18fe9cbbc318facf5adb47ae5907d842f9"
|
|
9
9
|
}
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Test strategy, coverage gates, deterministic tests, mocking discipline, regression-first bug fixing, and test-suite organization (layout by type/domain, mirrored paths, file naming, fixture/helper placement).",
|
|
6
6
|
"updated": "2026-06-16T17:11:49+02:00",
|
|
7
|
-
"cliVersion": "1.33.
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
8
|
"sha": "3bdf591057b760f674fb2b1425f63acb426cda2c4f042e1a74c5a5d3807df664"
|
|
9
9
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: validation-policy
|
|
3
|
-
description: Strict frontend + backend schema validation (Zod or equivalent), normalization before validation (one shared normalizer on both sides - trim, lowercase the email, capitalize each word of a name, canonicalize a profile link or handle) and the rule that a check which cannot fail is not validation, schema consistency between client and server, and safe client-facing error handling. Use when handling any external input - forms, API request bodies, query params, CLI args, file parsing, or third-party payloads.
|
|
3
|
+
description: Strict frontend + backend schema validation (Zod or equivalent), normalization before validation (one shared normalizer on both sides - trim, lowercase the email, capitalize each word of a name, canonicalize a profile link or handle) and the rule that a check which cannot fail is not validation, cross-field rules declared on the object schema rather than the field (password against the email or username, confirmation fields, date ranges), schema consistency between client and server, and safe client-facing error handling. Use when handling any external input - forms, API request bodies, query params, CLI args, file parsing, or third-party payloads.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Validation & Error Handling Policy
|
|
@@ -83,6 +83,15 @@ Per field kind (defaults - override only with a reason):
|
|
|
83
83
|
- Before calling a field done, type three wrong values into it and confirm each is rejected. A validator nobody has watched fail is unverified.
|
|
84
84
|
- The same applies to a permissive fallback: an `.optional()` that swallows `""`, a `catch()` that returns a default, or a `refine` that returns `true` on anything it cannot parse.
|
|
85
85
|
|
|
86
|
+
### A rule about two fields lives on the object, not the field
|
|
87
|
+
|
|
88
|
+
Some rules cannot be expressed where the field is declared, because the field cannot see its siblings. `z.string().min(12)` for a password is a complete-looking schema that has no way to know the email sitting next to it, which is how "the password may not be your email" ends up unimplemented on a form that otherwise validates everything.
|
|
89
|
+
|
|
90
|
+
- Put cross-field rules on the OBJECT schema (`.superRefine`/`.refine` in Zod, a `model_validator` in Pydantic), and attach the error to the field the user has to change so it renders in that field's error slot, not at the top of the form.
|
|
91
|
+
- The recurring ones: password against the email, its local part, the username, the display name and the site name (normalized on both sides, per security-policy); the confirmation field against the password; a start date against an end date; a "one of these is required" pair.
|
|
92
|
+
- Give the object schema everything it needs to compare. On the server the identity usually comes from the account being modified rather than the request body, so build the schema with that value in scope (a factory that takes the identifiers and returns the schema) instead of trusting whatever the client sent.
|
|
93
|
+
- The client mirrors the same object schema so the conflict shows as the user types, and the server runs it again as the authority.
|
|
94
|
+
|
|
86
95
|
---
|
|
87
96
|
|
|
88
97
|
## Validation Standards
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"version": "1.4.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Strict frontend + backend schema validation, normalization before validation (shared normalizers: trim, lowercase email, capitalize names, canonicalize links and handles), schema consistency, and safe client-facing error handling.",
|
|
6
|
-
"updated": "2026-
|
|
7
|
-
"cliVersion": "1.33.
|
|
8
|
-
"sha": "
|
|
6
|
+
"updated": "2026-08-01T17:44:16+02:00",
|
|
7
|
+
"cliVersion": "1.33.3",
|
|
8
|
+
"sha": "225e1e26f4a49fac70714b1bebc104ea42143203cc8fcc9e3330b29f05b6e025"
|
|
9
9
|
}
|
package/bin/checksums.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"enigma-darwin-arm64": "
|
|
3
|
-
"enigma-linux-arm64": "
|
|
4
|
-
"enigma-linux-x64": "
|
|
5
|
-
"enigma-win32-x64.exe": "
|
|
2
|
+
"enigma-darwin-arm64": "286cca0f828af2df05e34bbf8e65739191fd32828800780f639adf033da3534e",
|
|
3
|
+
"enigma-linux-arm64": "97df04765c3aab4ea4ed2737409cee89a4993e82627016dbdd0b6beabff06fec",
|
|
4
|
+
"enigma-linux-x64": "b328e77ba40317e71a0c41cac400e70f7d74fb45c33491525137ed62f359a271",
|
|
5
|
+
"enigma-win32-x64.exe": "3cf574dc946b3d4294ef8c368d79e18acf42de6a65100a54d41a8b98f5375d06"
|
|
6
6
|
}
|
package/dist/guardrails.js
CHANGED
|
@@ -266,6 +266,57 @@ var BUILTIN_RULES = [
|
|
|
266
266
|
severity: "block",
|
|
267
267
|
skill: "security-policy"
|
|
268
268
|
},
|
|
269
|
+
{
|
|
270
|
+
id: "sec-password-identity-match",
|
|
271
|
+
label: "A new password is not the account's own identity",
|
|
272
|
+
files: ["*.tsx", "*.jsx", "*.vue", "*.svelte", "*.astro", "*.html", "*.htm", "*.ts", "*.js"],
|
|
273
|
+
excludeFiles: [
|
|
274
|
+
"*.test.*",
|
|
275
|
+
"*.spec.*",
|
|
276
|
+
"**/tests/**",
|
|
277
|
+
"**/__tests__/**",
|
|
278
|
+
"**/stories/**",
|
|
279
|
+
"*.stories.*",
|
|
280
|
+
"*.min.js",
|
|
281
|
+
"**/dist/**",
|
|
282
|
+
"**/build/**",
|
|
283
|
+
"**/_build/**",
|
|
284
|
+
"**/node_modules/**",
|
|
285
|
+
"**/vendor/**",
|
|
286
|
+
"dist/**",
|
|
287
|
+
"build/**",
|
|
288
|
+
"_build/**",
|
|
289
|
+
"node_modules/**",
|
|
290
|
+
"vendor/**"
|
|
291
|
+
],
|
|
292
|
+
scope: "file",
|
|
293
|
+
// The twin of sec-password-breach-check over the same selector, for the same reason the
|
|
294
|
+
// name rules are two: one `absent` cannot express "breach check AND identity check", and
|
|
295
|
+
// a file that does one is routinely missing the other. Same precision inheritance -
|
|
296
|
+
// `autocomplete="new-password"` marks a password being CREATED and nothing else.
|
|
297
|
+
// The `absent` set is deliberately NOT `email|username`: every sign-up form on earth
|
|
298
|
+
// mentions both, so keying on them would clear the rule everywhere it matters. It clears
|
|
299
|
+
// only on evidence of a COMPARISON - zxcvbn fed the user's own inputs (advisory, but a
|
|
300
|
+
// form gating on its score is a real implementation), Django's similarity validator, a
|
|
301
|
+
// helper named for the check, or password and an identifier on the same line either side
|
|
302
|
+
// of an equality/containment operator.
|
|
303
|
+
pattern: `autocomplete=\\{?["']new-password["']`,
|
|
304
|
+
absent: "userInputs|user_inputs|UserAttributeSimilarity|sameAs(?:Email|Username|Identity)|matchesIdentity|containsIdentity|identityMatch|notIdentity|personalInfo|(?:password|passwd|pwd)[^\\n]{0,60}(?:===|==|!==|\\.includes\\(|\\.indexOf\\(|\\.startsWith\\(|localeCompare)[^\\n]{0,60}(?:email|username|user_?name|handle)|(?:email|username|user_?name|handle)[^\\n]{0,60}(?:===|==|!==|\\.includes\\(|\\.indexOf\\(|\\.startsWith\\(|localeCompare)[^\\n]{0,60}(?:password|passwd|pwd)|enigma:allow-identity-password",
|
|
305
|
+
message: "A password is created here with nothing stopping it from being the account's own identity. `Fjrg2007` for the user `fjrg2007` is one guess for anyone who knows the email address. Refuse a candidate that equals, contains (4 characters or more), or closely resembles the email, its local part, the username, the display name or the site name - comparing NORMALIZED values on both sides (lowercase, trim, NFKD then strip accents, drop everything that is not a letter or a digit), so `F.J.R.G_2007` and `fjrg2007` are the same string and casing is never a difference. Declare it on the OBJECT schema, since a password field cannot see the email beside it, and run it again on the server where the real identity lives. A strength meter fed `userInputs` scores this badly but is advisory - keep the refusal as its own rule. For a flow with no identity to compare against, add an `enigma:allow-identity-password` note (security-policy, validation-policy).",
|
|
306
|
+
severity: "block",
|
|
307
|
+
skill: "security-policy"
|
|
308
|
+
},
|
|
309
|
+
// NOTE: no rule for the navigation conventions - nav entries carrying icons, a long nav
|
|
310
|
+
// grouped into labelled sections, and a Cmd/Ctrl+K command palette once the app has enough
|
|
311
|
+
// to hunt through. All three were measured and rejected; they live in frontend-policy's
|
|
312
|
+
// "Navigation Is Structured, Not A Growing List" and "Search & Filtering" sections only.
|
|
313
|
+
// The signature would have to be DENSITY - a file rendering many destinations - and density
|
|
314
|
+
// does not separate the app shell (where these belong) from a landing page or a docs page
|
|
315
|
+
// (where an icon per link and a palette would both be wrong). Measured over the corpus:
|
|
316
|
+
// 174 UI files, 29 with 8 or more links, and of the 4 with no search affordance every one
|
|
317
|
+
// is marketing or static docs - zero true positives, the same evidence that rejected the
|
|
318
|
+
// no-op-save and pinned-sidebar rules. Keying on a *sidebar*/*nav* filename instead found
|
|
319
|
+
// 2 candidates in the whole corpus, both terminal (ink) menus, so it has no signal either.
|
|
269
320
|
{
|
|
270
321
|
id: "fe-tracking-before-consent",
|
|
271
322
|
label: "Non-essential tracking waits for consent",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "enigma-cli",
|
|
3
|
-
"version": "1.33.
|
|
3
|
+
"version": "1.33.3",
|
|
4
4
|
"description": "Everything you need to work with a coding agent: install shared policy skills for Claude Code, OpenAI Codex and opencode, and set up portable git security hooks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|