waitsec 0.5.0 → 0.5.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.
@@ -113,6 +113,44 @@ Use this guide when creating a personal developer portfolio, founder bio, or eng
113
113
 
114
114
  ---
115
115
 
116
+ ## SEO, GEO & Structured Data (About & Portfolio)
117
+
118
+ - Title: full name plus role, for example "Ilyas Mukhlisin, Backend Engineer", about 50 to 60 characters.
119
+ - Meta description: who you are, what you build, and how to reach you, about 140 to 160 characters.
120
+ - One `<h1>` with your name.
121
+ - Server-render the bio, project list, and contact email.
122
+ - Add `sameAs` links to GitHub, LinkedIn, and X so engines can confirm the identity.
123
+ - Describe each project with a concrete outcome, not a vague claim, so answer engines have something to quote.
124
+
125
+ ### JSON-LD for an About or Portfolio Page
126
+
127
+ ```html
128
+ <script type="application/ld+json">
129
+ {
130
+ "@context": "https://schema.org",
131
+ "@type": "ProfilePage",
132
+ "url": "https://example.com/about",
133
+ "mainEntity": {
134
+ "@type": "Person",
135
+ "name": "Your Name",
136
+ "jobTitle": "Backend Engineer",
137
+ "url": "https://example.com",
138
+ "image": "https://example.com/images/avatar.jpg",
139
+ "worksFor": { "@type": "Organization", "name": "Fastro" },
140
+ "knowsAbout": ["Laravel", "PostgreSQL", "API design"],
141
+ "sameAs": [
142
+ "https://github.com/username",
143
+ "https://www.linkedin.com/in/username"
144
+ ]
145
+ }
146
+ }
147
+ </script>
148
+ ```
149
+
150
+ When the about page is part of a company site, add an `Organization` node and connect it with `worksFor` or `memberOf`.
151
+
152
+ ---
153
+
116
154
  ## Pre-Flight Checklist for About Me & Portfolio Pages
117
155
 
118
156
  - [ ] Are all fake animated skill percentage bars removed in favor of clean categorical tags?
@@ -120,3 +158,4 @@ Use this guide when creating a personal developer portfolio, founder bio, or eng
120
158
  - [ ] Does every featured project have a working live link or public repository link?
121
159
  - [ ] Is there a direct, clickable `mailto:` link clearly visible on the page?
122
160
  - [ ] Do project cards collapse into a clean single vertical stack on mobile screens?
161
+ - [ ] Is there a valid ProfilePage with Person data, including sameAs links to real profiles?
@@ -95,6 +95,68 @@ Use this guide when creating a single article view, blog post template, or markd
95
95
 
96
96
  ---
97
97
 
98
+ ## SEO, GEO & Structured Data (Article Page)
99
+
100
+ - Title: the article headline, optionally with the site name, about 50 to 60 characters.
101
+ - Meta description: an honest summary of the article, about 140 to 160 characters.
102
+ - One `<h1>` that matches the headline.
103
+ - State the article's main answer in the first paragraph, before background detail.
104
+ - Use question-shaped `<h2>` and `<h3>` headings, each followed by a direct answer.
105
+ - Show `datePublished` and `dateModified` visibly near the top, and keep both in the schema.
106
+ - Give the cover image descriptive alt text and an absolute URL.
107
+ - Keep the article body server-rendered.
108
+
109
+ ### JSON-LD for an Article Page
110
+
111
+ The `Article` type is safe, while `BlogPosting` is more specific for blog posts. Use the one that matches the page. Add `BreadcrumbList` so search engines can show the path.
112
+
113
+ ```html
114
+ <script type="application/ld+json">
115
+ {
116
+ "@context": "https://schema.org",
117
+ "@graph": [
118
+ {
119
+ "@type": "BlogPosting",
120
+ "headline": "Why Errors Appear as Codes and How to Read Them",
121
+ "description": "A short, honest summary of what the article explains.",
122
+ "image": "https://example.com/storage/blog-thumbnails/cover.webp",
123
+ "datePublished": "2026-09-04T09:56:00+07:00",
124
+ "dateModified": "2026-09-04T09:56:00+07:00",
125
+ "author": {
126
+ "@type": "Person",
127
+ "name": "Ilyas Mukhlisin",
128
+ "url": "https://example.com/about"
129
+ },
130
+ "publisher": {
131
+ "@type": "Organization",
132
+ "name": "Fikselink",
133
+ "logo": {
134
+ "@type": "ImageObject",
135
+ "url": "https://example.com/images/logo.png"
136
+ }
137
+ },
138
+ "mainEntityOfPage": {
139
+ "@type": "WebPage",
140
+ "@id": "https://example.com/blog/post/why-errors-appear-as-codes"
141
+ },
142
+ "articleSection": "Technology",
143
+ "keywords": "error code, HTTP status code, how to read errors"
144
+ },
145
+ {
146
+ "@type": "BreadcrumbList",
147
+ "itemListElement": [
148
+ { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com" },
149
+ { "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://example.com/blog" },
150
+ { "@type": "ListItem", "position": 3, "name": "Why Errors Appear as Codes", "item": "https://example.com/blog/post/why-errors-appear-as-codes" }
151
+ ]
152
+ }
153
+ ]
154
+ }
155
+ </script>
156
+ ```
157
+
158
+ ---
159
+
98
160
  ## Pre-Flight Checklist for Single Article Pages
99
161
 
100
162
  - [ ] Is the article reading width capped at `max-w-3xl` or `65-75ch` to prevent eye strain?
@@ -102,3 +164,5 @@ Use this guide when creating a single article view, blog post template, or markd
102
164
  - [ ] Is body paragraph line-height set to relaxed (`leading-relaxed` / 1.75)?
103
165
  - [ ] Are subheadings given generous top margin (`mt-10` to `mt-12`) to separate topics clearly?
104
166
  - [ ] Is there a clear back-navigation link at the top of the article?
167
+ - [ ] Does the first paragraph answer the main question directly?
168
+ - [ ] Is there a valid BlogPosting or Article block with author, publisher, and ISO 8601 dates?
@@ -0,0 +1,138 @@
1
+ # Auth Pages Blueprint (waitsec-pagemaker)
2
+
3
+ Use this guide when creating login, register, forgot password, reset password, remember me, or account lockout and rate-limit screens. These pages are private by nature, so the goal is clarity, safety, and a short path back into the product.
4
+
5
+ ---
6
+
7
+ ## Complete Page Anatomy & Responsive Flow
8
+
9
+ ### 1. Login Page
10
+ * **Desktop (1024px+):** Centered card (`max-w-md`) or a 2-column split with the form on the left and a solid brand panel on the right. No busy background.
11
+ * **Mobile (<768px):** Single column. Brand mark on top, then the form. Inputs and the primary button are full width.
12
+ * Fields: email or username, password with a show or hide toggle, a "Remember me" checkbox, the primary "Sign in" button, and a secondary "Forgot password?" link.
13
+ * One primary action only. Social login buttons appear only when a real provider is wired up, and they sit below a labeled divider.
14
+
15
+ ### 2. Register Page
16
+ * Minimal fields: name, email, password, and confirm password (or a single password field with a visible rules checklist).
17
+ * Show password rules as a short checklist and validate as the user types.
18
+ * Terms or privacy checkbox only when it is legally required.
19
+ * Primary "Create account", with a clear link back to login.
20
+
21
+ ### 3. Forgot and Reset Password
22
+ * Forgot: one email field and one primary "Send reset link" button. Always show the same neutral success message, whether or not the email exists.
23
+ * Reset: new password plus confirm. The token is handled server-side, and the page notes that the link expires.
24
+
25
+ ### 4. Remember Me
26
+ * Label states the exact duration, for example "Remember me for 30 days".
27
+ * Unchecked by default on shared or public devices.
28
+ * Never store tokens in `localStorage` or `sessionStorage`. Use secure, httpOnly, sameSite cookies set by the server.
29
+
30
+ ### 5. Account Locked and Rate Limit Blocked
31
+ * Explain what happened in plain words, how long the lock lasts, and the next step.
32
+ * Provide one recovery action: retry when the countdown ends, reset the password, or contact support.
33
+ * Show a live countdown and keep the submit button disabled until it ends.
34
+ * Render this as its own clear state, not a generic error page.
35
+
36
+ ---
37
+
38
+ ## Interaction and Security Rules (Non-Negotiable)
39
+
40
+ - Never reveal whether an account exists. Use the same message for an unknown email and a wrong password.
41
+ - Enforce the rate limit on the server. The UI only reflects the remaining wait time.
42
+ - Use correct autocomplete attributes: `autocomplete="email"`, `autocomplete="current-password"` for login, and `autocomplete="new-password"` for register and reset.
43
+ - Keep password fields accessible with visible labels and a show or hide toggle.
44
+ - Errors appear as a summary at the top of the form and, where useful, inline under the field.
45
+ - Use `aria-live="polite"` for the lockout countdown and for async error messages.
46
+ - Give every control at least a 44px by 44px target on mobile.
47
+ - Set input font size to at least 16px on mobile so iOS does not zoom on focus.
48
+ - Center the card with `min-h` and normal document flow, never a fixed `h-screen` that traps the form when the keyboard opens.
49
+
50
+ ---
51
+
52
+ ## Detailed Pitfalls & The 5-Point Rule
53
+
54
+ ### 1. User Enumeration
55
+
56
+ * **The Bad Habit:** The login form says "Email not found" for an unknown address and "Wrong password" for a known one.
57
+ * **The Problem:** The different responses let an attacker test which emails have accounts.
58
+ * **Why It Fails:** It turns the login page into an account discovery tool and invites credential stuffing against real users.
59
+ * **Clean Fix:** Return one generic message such as "Email or password is incorrect" for both cases, and keep the response time consistent.
60
+ * **The Waitsec Way:** Treat account existence as private data. Say less to stay safe.
61
+
62
+ ### 2. Lockout With No Explanation or Recovery
63
+
64
+ * **The Bad Habit:** After too many attempts the form silently stops working or returns a blank error.
65
+ * **The Problem:** The user assumes the site is broken and keeps retrying, which extends the lock.
66
+ * **Why It Fails:** Frustration grows, support load rises, and legitimate users are locked out with no path back.
67
+ * **Clean Fix:** Show a clear locked state with the reason, a live countdown, and one recovery action.
68
+ * **The Waitsec Way:** A block must explain itself and offer a way out.
69
+
70
+ ### 3. Insecure Remember Me
71
+
72
+ * **The Bad Habit:** Storing a long-lived token in `localStorage` so the checkbox "just works".
73
+ * **The Problem:** Any script on the page can read the token, and a stolen token keeps the session alive.
74
+ * **Why It Fails:** It turns a small XSS into a permanent account takeover.
75
+ * **Clean Fix:** Use secure, httpOnly, sameSite cookies set by the server, with a sensible expiry, and rotate the token on use.
76
+ * **The Waitsec Way:** Convenience must never weaken the session.
77
+
78
+ ### 4. Password Fields That Fail on Mobile
79
+
80
+ * **The Bad Habit:** A password input with no show or hide toggle, tiny text, and no correct autocomplete hint.
81
+ * **The Problem:** Users mistype, cannot verify, and the browser fills the wrong field or the wrong kind of value.
82
+ * **Why It Fails:** Failed logins pile up, and some users abandon the form entirely.
83
+ * **Clean Fix:** Add a show or hide toggle, use at least 16px text, and set the correct `autocomplete` attribute for each field.
84
+ * **The Waitsec Way:** Remove friction from the most repeated action in the product.
85
+
86
+ ### 5. Fixed h-screen Form Trap
87
+
88
+ * **The Bad Habit:** Centering the card inside `h-screen flex items-center`.
89
+ * **The Problem:** When the mobile keyboard opens, the viewport shrinks and the input scrolls out of view.
90
+ * **Why It Fails:** Users cannot see what they type or reach the submit button.
91
+ * **Clean Fix:** Use `min-h-screen` with normal flow and enough bottom padding so the form scrolls above the keyboard.
92
+ * **The Waitsec Way:** Let the page scroll. Never lock the height of a form on mobile.
93
+
94
+ ### 6. No Loading or Disabled State
95
+
96
+ * **The Bad Habit:** The submit button stays active while the request runs.
97
+ * **The Problem:** Users click several times and send duplicate login or register requests.
98
+ * **Why It Fails:** It triggers avoidable rate limits and adds confusing error states.
99
+ * **Clean Fix:** Disable the button on submit, show a spinner or "Signing in..." label, and re-enable it on failure.
100
+ * **The Waitsec Way:** Every submit needs feedback and a single-flight guard.
101
+
102
+ ---
103
+
104
+ ## SEO, GEO & Structured Data (Auth Pages)
105
+
106
+ - Auth pages should not rank. Set `<meta name="robots" content="noindex, nofollow">`.
107
+ - Keep a simple title ("Sign in" or "Create account") plus the brand, and skip promotional copy.
108
+ - Exclude auth pages from the sitemap and avoid linking to them as content.
109
+ - Because this is a private page, use a minimal `WebPage` node. Do not add rich result types that a login page cannot support.
110
+
111
+ ```html
112
+ <script type="application/ld+json">
113
+ {
114
+ "@context": "https://schema.org",
115
+ "@type": "WebPage",
116
+ "name": "Sign in",
117
+ "url": "https://example.com/login",
118
+ "isPartOf": {
119
+ "@type": "WebSite",
120
+ "url": "https://example.com"
121
+ }
122
+ }
123
+ </script>
124
+ ```
125
+
126
+ ---
127
+
128
+ ## Pre-Flight Checklist for Auth Pages
129
+
130
+ - [ ] Is `<meta name="robots" content="noindex, nofollow">` present, and the page excluded from the sitemap?
131
+ - [ ] Does the page avoid revealing whether an account exists, with the same message and timing for all failures?
132
+ - [ ] Do password fields have a show or hide toggle and the correct `autocomplete` attribute?
133
+ - [ ] Does "Remember me" state its duration and default to off on shared devices?
134
+ - [ ] Are session tokens kept out of `localStorage` and placed in secure httpOnly cookies?
135
+ - [ ] Does the locked or rate-limited state explain the reason, show a countdown, and offer one recovery action?
136
+ - [ ] Is the submit button disabled during the request to prevent double submits?
137
+ - [ ] Does the form use `min-h-screen` with normal scrolling instead of a fixed `h-screen`?
138
+ - [ ] Is a minimal `WebPage` JSON-LD block present and valid?
@@ -112,6 +112,50 @@ Every article preview card must contain:
112
112
 
113
113
  ---
114
114
 
115
+ ## SEO, GEO & Structured Data (Blog Index)
116
+
117
+ - Title: blog name plus its topic focus, about 50 to 60 characters.
118
+ - Meta description: what the blog covers and who it helps, about 140 to 160 characters.
119
+ - One `<h1>` for the index title, and `<h2>` for each article title.
120
+ - Server-render article titles, excerpts, and dates so crawlers read them without JavaScript.
121
+ - Link each card with the article title as anchor text, not "read more".
122
+ - Point a canonical tag at the canonical page of the listing, and keep paginated pages self-canonical.
123
+ - Keep the publish date and reading time visible, because freshness matters to both search and answer engines.
124
+
125
+ ### JSON-LD for a Blog Index
126
+
127
+ ```html
128
+ <script type="application/ld+json">
129
+ {
130
+ "@context": "https://schema.org",
131
+ "@type": "CollectionPage",
132
+ "name": "Engineering Journal",
133
+ "url": "https://example.com/blog",
134
+ "description": "Articles about software architecture, security, and frontend engineering.",
135
+ "isPartOf": { "@type": "WebSite", "url": "https://example.com" },
136
+ "mainEntity": {
137
+ "@type": "ItemList",
138
+ "itemListElement": [
139
+ {
140
+ "@type": "ListItem",
141
+ "position": 1,
142
+ "url": "https://example.com/blog/scaling-sqlite"
143
+ },
144
+ {
145
+ "@type": "ListItem",
146
+ "position": 2,
147
+ "url": "https://example.com/blog/reading-stack-traces"
148
+ }
149
+ ]
150
+ }
151
+ }
152
+ </script>
153
+ ```
154
+
155
+ Use `Blog` instead of `CollectionPage` when the listing is a true blog. Include one `ListItem` per visible article, in the same order shown on the page.
156
+
157
+ ---
158
+
115
159
  ## Pre-Flight Checklist for Blog Index Pages
116
160
 
117
161
  - [ ] Do article cards stack into a clean single column on mobile without horizontal scrolling?
@@ -119,3 +163,5 @@ Every article preview card must contain:
119
163
  - [ ] Are article excerpts clamped to 2 or 3 lines so cards maintain a predictable height?
120
164
  - [ ] Can visitors comfortably reach the footer without being trapped by automatic infinite scroll?
121
165
  - [ ] Are publication dates and reading estimates clearly legible with strong contrast?
166
+ - [ ] Does the page expose a valid CollectionPage or Blog ItemList schema?
167
+ - [ ] Do article cards use the article title as anchor text instead of "read more"?
@@ -84,9 +84,9 @@ Never demand phone numbers, fax numbers, company size, or physical mailing addre
84
84
  * **Why It Fails:** Software must always confirm state changes. Silent forms destroy confidence and generate duplicate tickets.
85
85
  * **Clean Fix:** Disable the submit button immediately on click, show a loading state, and render an unmistakable success card:
86
86
  ```html
87
- <!-- Button Loading State -->
87
+ <!-- Button Loading State: the spinner icon comes from the icon library, never inline SVG -->
88
88
  <button disabled class="w-full py-3 bg-neutral-700 text-white font-medium rounded-lg cursor-not-allowed flex items-center justify-center gap-2">
89
- <svg class="animate-spin h-4 w-4 text-white" viewBox="0 0 24 24">...</svg>
89
+ <i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i>
90
90
  <span>Sending...</span>
91
91
  </button>
92
92
 
@@ -113,6 +113,43 @@ Never demand phone numbers, fax numbers, company size, or physical mailing addre
113
113
 
114
114
  ---
115
115
 
116
+ ## SEO, GEO & Structured Data (Contact Page)
117
+
118
+ - Title: "Contact" plus the brand name, about 50 to 60 characters.
119
+ - Meta description: how to reach the team and the expected response time, about 140 to 160 characters.
120
+ - One `<h1>` such as "Get in touch".
121
+ - Print the email and address as visible text, not only inside the form, so they can be extracted.
122
+ - Server-render the contact details, the email link, and the response-time note.
123
+ - Keep the page focused on contact, and avoid unrelated marketing sections.
124
+
125
+ ### JSON-LD for a Contact Page
126
+
127
+ ```html
128
+ <script type="application/ld+json">
129
+ {
130
+ "@context": "https://schema.org",
131
+ "@type": "ContactPage",
132
+ "name": "Contact Us",
133
+ "url": "https://example.com/contact",
134
+ "mainEntity": {
135
+ "@type": "Organization",
136
+ "name": "Example Inc",
137
+ "url": "https://example.com",
138
+ "contactPoint": {
139
+ "@type": "ContactPoint",
140
+ "contactType": "customer support",
141
+ "email": "hello@example.com",
142
+ "availableLanguage": ["English", "Indonesian"]
143
+ }
144
+ }
145
+ }
146
+ </script>
147
+ ```
148
+
149
+ Add the organization `logo` and a `PostalAddress` when a physical address is shown. Only include channels that are actually displayed on the page.
150
+
151
+ ---
152
+
116
153
  ## Pre-Flight Checklist for Contact Pages
117
154
 
118
155
  - [ ] Is the form limited to 3 or 4 essential fields (Name, Email, Message)?
@@ -121,3 +158,5 @@ Never demand phone numbers, fax numbers, company size, or physical mailing addre
121
158
  - [ ] Does the page display an unmistakable success banner after submission?
122
159
  - [ ] Is there a direct, visible email address (`mailto:`) provided as an alternative contact method?
123
160
  - [ ] Does the form have enough bottom padding to clear mobile virtual keyboards?
161
+ - [ ] Is there a valid ContactPage with Organization and ContactPoint schema?
162
+ - [ ] Are the email and address visible as text, not only inside the form?
@@ -143,6 +143,72 @@ Use this guide when creating a marketing page, product launch screen, or SaaS ho
143
143
 
144
144
  ---
145
145
 
146
+ ## SEO, GEO & Structured Data (Landing Page)
147
+
148
+ State what the product is and who it is for within the first two sentences of the hero. Answer engines quote those lines, so make them complete and direct.
149
+
150
+ - Title: product name plus one clear benefit, about 50 to 60 characters.
151
+ - Meta description: what the product does and who it is for, about 140 to 160 characters.
152
+ - One `<h1>` that carries the core value proposition in plain words.
153
+ - Server-render the hero headline, subheadline, and primary CTA.
154
+ - Add an FAQ block with question-shaped `<h3>` headings. Start each answer with a direct one-sentence response.
155
+ - Keep the Open Graph image at 1200x630 and use an absolute URL.
156
+
157
+ ### JSON-LD for a Landing Page
158
+
159
+ ```html
160
+ <script type="application/ld+json">
161
+ {
162
+ "@context": "https://schema.org",
163
+ "@graph": [
164
+ {
165
+ "@type": "Organization",
166
+ "@id": "https://example.com/#organization",
167
+ "name": "Example Inc",
168
+ "url": "https://example.com",
169
+ "logo": "https://example.com/images/logo.png"
170
+ },
171
+ {
172
+ "@type": "WebSite",
173
+ "@id": "https://example.com/#website",
174
+ "url": "https://example.com",
175
+ "name": "Example",
176
+ "publisher": { "@id": "https://example.com/#organization" }
177
+ },
178
+ {
179
+ "@type": "SoftwareApplication",
180
+ "name": "Example App",
181
+ "applicationCategory": "BusinessApplication",
182
+ "operatingSystem": "Web",
183
+ "url": "https://example.com",
184
+ "offers": {
185
+ "@type": "Offer",
186
+ "price": "0",
187
+ "priceCurrency": "USD"
188
+ }
189
+ },
190
+ {
191
+ "@type": "FAQPage",
192
+ "mainEntity": [
193
+ {
194
+ "@type": "Question",
195
+ "name": "Is there a free plan?",
196
+ "acceptedAnswer": {
197
+ "@type": "Answer",
198
+ "text": "Yes. The free plan covers one project with no time limit."
199
+ }
200
+ }
201
+ ]
202
+ }
203
+ ]
204
+ }
205
+ </script>
206
+ ```
207
+
208
+ Include the `FAQPage` block only when the FAQ is actually visible on the page. Use `Product` instead of `SoftwareApplication` for a physical or ecommerce product.
209
+
210
+ ---
211
+
146
212
  ## Pre-Flight Checklist for Landing Pages
147
213
 
148
214
  - [ ] Does the hero section have exactly 1 high-contrast primary CTA button?
@@ -150,3 +216,5 @@ Use this guide when creating a marketing page, product launch screen, or SaaS ho
150
216
  - [ ] Are feature cards structured with varied visual weight (bento style) rather than copy-paste clones?
151
217
  - [ ] Is there zero horizontal page wobble when testing at 320px width?
152
218
  - [ ] Are all headlines scaled down comfortably on mobile to avoid breaking words into multiple lines?
219
+ - [ ] Is there one H1, a unique title, a meta description, and an absolute Open Graph image?
220
+ - [ ] Is the JSON-LD valid, type-appropriate, and free of validator errors?
@@ -21,22 +21,101 @@ Activate this skill whenever:
21
21
 
22
22
  ---
23
23
 
24
- ## Core Guardrails
25
-
26
- ### 1. Security Auditing & Access Boundaries
27
- - **Ownership & IDOR:** Validate tenant and user ownership on every database read, update, and delete operation. Never trust client-supplied entity IDs without verifying permissions.
28
- - **Sensitive Data Exposure:** Never return raw password hashes, internal server stack traces, or private keys in API responses. Use explicit serialization filters.
29
- - **Mass Assignment:** Whitelist fillable attributes explicitly on ORM models or request validators. Reject raw wildcard inserts.
30
-
31
- ### 2. Testing Discipline
32
- - **Assertion Reality:** Every test must assert concrete side-effects or state changes. Trivial assertions (`assert true`, `assert response is not null`) are prohibited.
33
- - **Mocking Boundaries:** Test actual business logic and database queries where feasible. Never mock the system under test to force an artificial passing run.
34
- - **Regression Tests:** When fixing an existing bug, write a failing reproduction test before applying the fix.
35
-
36
- ### 3. Data & Migration Integrity
37
- - **Rollback Safety:** Every database migration must include a working, tested down/rollback method.
38
- - **Zero Destructive Shortcuts:** Never drop tables or columns on production schemas without an explicit multi-step deprecation plan.
39
- - **Constraint Discipline:** Enforce foreign keys and uniqueness constraints at the database engine level, not solely in application memory.
24
+ ## Part 1: Security & Access Boundaries
25
+
26
+ ### 1. Trusting Client-Supplied IDs (IDOR)
27
+
28
+ * **The Bad Habit:** Fetching a record straight from a request ID, like `Order::find($id)`, without checking who owns it.
29
+ * **The Problem:** Any logged-in user can change the ID in the URL and read or edit someone else's record.
30
+ * **Why It Fails:** This is a direct data breach. One guessed number exposes private orders, messages, or invoices.
31
+ * **Clean Fix:** Scope every read, update, and delete to the authenticated user or tenant:
32
+ ```php
33
+ // Bad
34
+ $order = Order::find($id);
35
+
36
+ // Good
37
+ $order = auth()->user()->orders()->findOrFail($id);
38
+ ```
39
+ * **The Waitsec Way:** Never trust an ID that came from the client. Verify ownership on every data access.
40
+
41
+ ### 2. Leaking Sensitive Data in Responses
42
+
43
+ * **The Bad Habit:** Returning the whole model, including password hashes, internal flags, or stack traces.
44
+ * **The Problem:** The API response exposes fields the user should never see.
45
+ * **Why It Fails:** Leaked hashes and internal details hand attackers the first step of a break-in.
46
+ * **Clean Fix:** Return explicit, whitelisted fields only. Use serialization filters or resource classes.
47
+ * **The Waitsec Way:** Send the minimum the client needs. Everything else stays server-side.
48
+
49
+ ### 3. Mass Assignment
50
+
51
+ * **The Bad Habit:** Passing the whole request payload into `create()` or `update()`.
52
+ * **The Problem:** A crafted request can set fields the form never exposed, such as `isAdmin` or `price`.
53
+ * **Why It Fails:** Attackers change roles and prices with a single extra parameter, and the database accepts it.
54
+ * **Clean Fix:** Whitelist fillable attributes and pass only validated data:
55
+ ```php
56
+ // Forbidden
57
+ User::create($request->all());
58
+
59
+ // Required
60
+ User::create($request->validated());
61
+ ```
62
+ * **The Waitsec Way:** Accept only the fields you meant to accept. Never forward raw input to the database.
63
+
64
+ ---
65
+
66
+ ## Part 2: Testing Discipline
67
+
68
+ ### 4. Trivial Assertions
69
+
70
+ * **The Bad Habit:** Writing tests that assert `true`, assert the response is not null, or call an endpoint without checking the result.
71
+ * **The Problem:** The suite is green while the feature is broken.
72
+ * **Why It Fails:** A passing test that proves nothing gives false confidence. Bugs reach production behind a fake safety net.
73
+ * **Clean Fix:** Assert a concrete outcome: the row was created, the balance changed, the status code is 403, or the field equals the expected value.
74
+ * **The Waitsec Way:** A test must prove a behavior. If it cannot fail, it is not a test.
75
+
76
+ ### 5. Mocking the System Under Test
77
+
78
+ * **The Bad Habit:** Mocking the very class or query the test is supposed to verify.
79
+ * **The Problem:** The test only confirms that the mock returned what the mock was told to return.
80
+ * **Why It Fails:** Real bugs live in the code that got mocked away. The suite passes and the feature still fails.
81
+ * **Clean Fix:** Test real business logic and database behavior where feasible. Mock only true external boundaries.
82
+ * **The Waitsec Way:** Test the real thing. Put mocks at the edges, never over the subject.
83
+
84
+ ### 6. Fixing Without a Reproduction Test
85
+
86
+ * **The Bad Habit:** Fixing a reported bug immediately and moving on, with no test that captures the failure.
87
+ * **The Problem:** Nothing stops the same bug from returning in the next refactor.
88
+ * **Why It Fails:** Without a failing reproduction first, you cannot even prove the fix addresses the reported case.
89
+ * **Clean Fix:** Write a failing test that reproduces the bug, then fix until it passes, then keep the test.
90
+ * **The Waitsec Way:** A bug fix ships with proof. The reproduction test is that proof.
91
+
92
+ ---
93
+
94
+ ## Part 3: Data & Migration Integrity
95
+
96
+ ### 7. One-Way Migrations
97
+
98
+ * **The Bad Habit:** Writing an `up()` migration with no working `down()` rollback.
99
+ * **The Problem:** A bad deploy cannot be reversed cleanly.
100
+ * **Why It Fails:** When the migration corrupts or blocks data, the team is stuck with a manual, risky recovery under pressure.
101
+ * **Clean Fix:** Every migration gets a tested rollback that restores the previous schema state.
102
+ * **The Waitsec Way:** A migration is a two-way door. If you cannot safely go back, you cannot safely go forward.
103
+
104
+ ### 8. Destructive Schema Shortcuts
105
+
106
+ * **The Bad Habit:** Dropping a column or table in one migration to "clean up".
107
+ * **The Problem:** Live data disappears the moment the migration runs, with no recovery path.
108
+ * **Why It Fails:** Production data loss is permanent and often unrecoverable. One migration erases history.
109
+ * **Clean Fix:** Deprecate in steps: stop writing, keep reading, back up, wait, then drop in a later release.
110
+ * **The Waitsec Way:** Remove data only when you are certain nothing needs it. Prefer deprecation over destruction.
111
+
112
+ ### 9. Application-Only Constraints
113
+
114
+ * **The Bad Habit:** Enforcing uniqueness or relationships only in application code.
115
+ * **The Problem:** Race conditions and direct database writes slip past the checks.
116
+ * **Why It Fails:** Duplicate or orphaned rows appear even though the app "always checks". Data integrity decays over time.
117
+ * **Clean Fix:** Enforce foreign keys, unique indexes, and not-null rules at the database engine level.
118
+ * **The Waitsec Way:** The database is the last line of defense. Let it enforce the invariants.
40
119
 
41
120
  ---
42
121