@qball-inc/the-bulwark 1.0.0

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.
Files changed (175) hide show
  1. package/.claude-plugin/plugin.json +43 -0
  2. package/agents/bulwark-fix-validator.md +633 -0
  3. package/agents/bulwark-implementer.md +391 -0
  4. package/agents/bulwark-issue-analyzer.md +308 -0
  5. package/agents/bulwark-standards-reviewer.md +221 -0
  6. package/agents/plan-creation-architect.md +323 -0
  7. package/agents/plan-creation-eng-lead.md +352 -0
  8. package/agents/plan-creation-po.md +300 -0
  9. package/agents/plan-creation-qa-critic.md +334 -0
  10. package/agents/product-ideation-competitive-analyzer.md +298 -0
  11. package/agents/product-ideation-idea-validator.md +268 -0
  12. package/agents/product-ideation-market-researcher.md +292 -0
  13. package/agents/product-ideation-pattern-documenter.md +308 -0
  14. package/agents/product-ideation-segment-analyzer.md +303 -0
  15. package/agents/product-ideation-strategist.md +259 -0
  16. package/agents/statusline-setup.md +97 -0
  17. package/hooks/hooks.json +59 -0
  18. package/package.json +45 -0
  19. package/scripts/hooks/cleanup-stale.sh +13 -0
  20. package/scripts/hooks/enforce-quality.sh +166 -0
  21. package/scripts/hooks/implementer-quality.sh +256 -0
  22. package/scripts/hooks/inject-protocol.sh +52 -0
  23. package/scripts/hooks/suggest-pipeline.sh +175 -0
  24. package/scripts/hooks/track-pipeline-start.sh +37 -0
  25. package/scripts/hooks/track-pipeline-stop.sh +52 -0
  26. package/scripts/init-rules.sh +35 -0
  27. package/scripts/init.sh +151 -0
  28. package/skills/anthropic-validator/SKILL.md +607 -0
  29. package/skills/anthropic-validator/references/agents-checklist.md +131 -0
  30. package/skills/anthropic-validator/references/commands-checklist.md +102 -0
  31. package/skills/anthropic-validator/references/hooks-checklist.md +151 -0
  32. package/skills/anthropic-validator/references/mcp-checklist.md +136 -0
  33. package/skills/anthropic-validator/references/plugins-checklist.md +148 -0
  34. package/skills/anthropic-validator/references/skills-checklist.md +85 -0
  35. package/skills/assertion-patterns/SKILL.md +296 -0
  36. package/skills/bug-magnet-data/SKILL.md +284 -0
  37. package/skills/bug-magnet-data/context/cli-args.md +91 -0
  38. package/skills/bug-magnet-data/context/db-query.md +104 -0
  39. package/skills/bug-magnet-data/context/file-contents.md +103 -0
  40. package/skills/bug-magnet-data/context/http-body.md +91 -0
  41. package/skills/bug-magnet-data/context/process-spawn.md +123 -0
  42. package/skills/bug-magnet-data/data/booleans/boundaries.yaml +143 -0
  43. package/skills/bug-magnet-data/data/collections/arrays.yaml +114 -0
  44. package/skills/bug-magnet-data/data/collections/objects.yaml +123 -0
  45. package/skills/bug-magnet-data/data/concurrency/race-conditions.yaml +118 -0
  46. package/skills/bug-magnet-data/data/concurrency/state-machines.yaml +115 -0
  47. package/skills/bug-magnet-data/data/dates/boundaries.yaml +137 -0
  48. package/skills/bug-magnet-data/data/dates/invalid.yaml +132 -0
  49. package/skills/bug-magnet-data/data/dates/timezone.yaml +118 -0
  50. package/skills/bug-magnet-data/data/encoding/charset.yaml +79 -0
  51. package/skills/bug-magnet-data/data/encoding/normalization.yaml +105 -0
  52. package/skills/bug-magnet-data/data/formats/email.yaml +154 -0
  53. package/skills/bug-magnet-data/data/formats/json.yaml +187 -0
  54. package/skills/bug-magnet-data/data/formats/url.yaml +165 -0
  55. package/skills/bug-magnet-data/data/language-specific/javascript.yaml +182 -0
  56. package/skills/bug-magnet-data/data/language-specific/python.yaml +174 -0
  57. package/skills/bug-magnet-data/data/language-specific/rust.yaml +148 -0
  58. package/skills/bug-magnet-data/data/numbers/boundaries.yaml +161 -0
  59. package/skills/bug-magnet-data/data/numbers/precision.yaml +89 -0
  60. package/skills/bug-magnet-data/data/numbers/special.yaml +69 -0
  61. package/skills/bug-magnet-data/data/strings/boundaries.yaml +109 -0
  62. package/skills/bug-magnet-data/data/strings/injection.yaml +208 -0
  63. package/skills/bug-magnet-data/data/strings/special-chars.yaml +190 -0
  64. package/skills/bug-magnet-data/data/strings/unicode.yaml +139 -0
  65. package/skills/bug-magnet-data/references/external-lists.md +115 -0
  66. package/skills/bulwark-brainstorm/SKILL.md +563 -0
  67. package/skills/bulwark-brainstorm/references/at-teammate-prompts.md +60 -0
  68. package/skills/bulwark-brainstorm/references/role-critical-analyst.md +78 -0
  69. package/skills/bulwark-brainstorm/references/role-development-lead.md +66 -0
  70. package/skills/bulwark-brainstorm/references/role-product-delivery-lead.md +79 -0
  71. package/skills/bulwark-brainstorm/references/role-product-manager.md +62 -0
  72. package/skills/bulwark-brainstorm/references/role-project-sme.md +59 -0
  73. package/skills/bulwark-brainstorm/references/role-technical-architect.md +66 -0
  74. package/skills/bulwark-research/SKILL.md +298 -0
  75. package/skills/bulwark-research/references/viewpoint-contrarian.md +63 -0
  76. package/skills/bulwark-research/references/viewpoint-direct-investigation.md +62 -0
  77. package/skills/bulwark-research/references/viewpoint-first-principles.md +65 -0
  78. package/skills/bulwark-research/references/viewpoint-practitioner.md +62 -0
  79. package/skills/bulwark-research/references/viewpoint-prior-art.md +66 -0
  80. package/skills/bulwark-scaffold/SKILL.md +330 -0
  81. package/skills/bulwark-statusline/SKILL.md +161 -0
  82. package/skills/bulwark-statusline/scripts/statusline.sh +144 -0
  83. package/skills/bulwark-verify/SKILL.md +519 -0
  84. package/skills/code-review/SKILL.md +428 -0
  85. package/skills/code-review/examples/anti-patterns/linting.ts +181 -0
  86. package/skills/code-review/examples/anti-patterns/security.ts +91 -0
  87. package/skills/code-review/examples/anti-patterns/standards.ts +195 -0
  88. package/skills/code-review/examples/anti-patterns/type-safety.ts +108 -0
  89. package/skills/code-review/examples/recommended/linting.ts +195 -0
  90. package/skills/code-review/examples/recommended/security.ts +154 -0
  91. package/skills/code-review/examples/recommended/standards.ts +231 -0
  92. package/skills/code-review/examples/recommended/type-safety.ts +181 -0
  93. package/skills/code-review/frameworks/angular.md +218 -0
  94. package/skills/code-review/frameworks/django.md +235 -0
  95. package/skills/code-review/frameworks/express.md +207 -0
  96. package/skills/code-review/frameworks/flask.md +298 -0
  97. package/skills/code-review/frameworks/generic.md +146 -0
  98. package/skills/code-review/frameworks/react.md +152 -0
  99. package/skills/code-review/frameworks/vue.md +244 -0
  100. package/skills/code-review/references/linting-patterns.md +221 -0
  101. package/skills/code-review/references/security-patterns.md +125 -0
  102. package/skills/code-review/references/standards-patterns.md +246 -0
  103. package/skills/code-review/references/type-safety-patterns.md +130 -0
  104. package/skills/component-patterns/SKILL.md +131 -0
  105. package/skills/component-patterns/references/pattern-cli-command.md +118 -0
  106. package/skills/component-patterns/references/pattern-database.md +166 -0
  107. package/skills/component-patterns/references/pattern-external-api.md +139 -0
  108. package/skills/component-patterns/references/pattern-file-parser.md +168 -0
  109. package/skills/component-patterns/references/pattern-http-server.md +162 -0
  110. package/skills/component-patterns/references/pattern-process-spawner.md +133 -0
  111. package/skills/continuous-feedback/SKILL.md +327 -0
  112. package/skills/continuous-feedback/references/collect-instructions.md +81 -0
  113. package/skills/continuous-feedback/references/specialize-code-review.md +82 -0
  114. package/skills/continuous-feedback/references/specialize-general.md +98 -0
  115. package/skills/continuous-feedback/references/specialize-test-audit.md +81 -0
  116. package/skills/create-skill/SKILL.md +359 -0
  117. package/skills/create-skill/references/agent-conventions.md +194 -0
  118. package/skills/create-skill/references/agent-template.md +195 -0
  119. package/skills/create-skill/references/content-guidance.md +291 -0
  120. package/skills/create-skill/references/decision-framework.md +124 -0
  121. package/skills/create-skill/references/template-pipeline.md +217 -0
  122. package/skills/create-skill/references/template-reference-heavy.md +111 -0
  123. package/skills/create-skill/references/template-research.md +210 -0
  124. package/skills/create-skill/references/template-script-driven.md +172 -0
  125. package/skills/create-skill/references/template-simple.md +80 -0
  126. package/skills/create-subagent/SKILL.md +353 -0
  127. package/skills/create-subagent/references/agent-conventions.md +268 -0
  128. package/skills/create-subagent/references/content-guidance.md +232 -0
  129. package/skills/create-subagent/references/decision-framework.md +134 -0
  130. package/skills/create-subagent/references/template-single-agent.md +192 -0
  131. package/skills/fix-bug/SKILL.md +241 -0
  132. package/skills/governance-protocol/SKILL.md +116 -0
  133. package/skills/init/SKILL.md +341 -0
  134. package/skills/issue-debugging/SKILL.md +385 -0
  135. package/skills/issue-debugging/references/anti-patterns.md +245 -0
  136. package/skills/issue-debugging/references/debug-report-schema.md +227 -0
  137. package/skills/mock-detection/SKILL.md +511 -0
  138. package/skills/mock-detection/references/false-positive-prevention.md +402 -0
  139. package/skills/mock-detection/references/stub-patterns.md +236 -0
  140. package/skills/pipeline-templates/SKILL.md +215 -0
  141. package/skills/pipeline-templates/references/code-change-workflow.md +277 -0
  142. package/skills/pipeline-templates/references/code-review.md +336 -0
  143. package/skills/pipeline-templates/references/fix-validation.md +421 -0
  144. package/skills/pipeline-templates/references/new-feature.md +335 -0
  145. package/skills/pipeline-templates/references/research-brainstorm.md +161 -0
  146. package/skills/pipeline-templates/references/research-planning.md +257 -0
  147. package/skills/pipeline-templates/references/test-audit.md +389 -0
  148. package/skills/pipeline-templates/references/test-execution-fix.md +238 -0
  149. package/skills/plan-creation/SKILL.md +497 -0
  150. package/skills/product-ideation/SKILL.md +372 -0
  151. package/skills/product-ideation/references/analysis-frameworks.md +161 -0
  152. package/skills/session-handoff/SKILL.md +139 -0
  153. package/skills/session-handoff/references/examples.md +223 -0
  154. package/skills/setup-lsp/SKILL.md +312 -0
  155. package/skills/setup-lsp/references/server-registry.md +85 -0
  156. package/skills/setup-lsp/references/troubleshooting.md +135 -0
  157. package/skills/subagent-output-templating/SKILL.md +415 -0
  158. package/skills/subagent-output-templating/references/examples.md +440 -0
  159. package/skills/subagent-prompting/SKILL.md +364 -0
  160. package/skills/subagent-prompting/references/examples.md +342 -0
  161. package/skills/test-audit/SKILL.md +531 -0
  162. package/skills/test-audit/references/known-limitations.md +41 -0
  163. package/skills/test-audit/references/priority-classification.md +30 -0
  164. package/skills/test-audit/references/prompts/deep-mode-detection.md +83 -0
  165. package/skills/test-audit/references/prompts/synthesis.md +57 -0
  166. package/skills/test-audit/references/rewrite-instructions.md +46 -0
  167. package/skills/test-audit/references/schemas/audit-output.yaml +100 -0
  168. package/skills/test-audit/references/schemas/diagnostic-output.yaml +49 -0
  169. package/skills/test-audit/scripts/data-flow-analyzer.ts +509 -0
  170. package/skills/test-audit/scripts/integration-mock-detector.ts +462 -0
  171. package/skills/test-audit/scripts/package.json +20 -0
  172. package/skills/test-audit/scripts/skip-detector.ts +211 -0
  173. package/skills/test-audit/scripts/verification-counter.ts +295 -0
  174. package/skills/test-classification/SKILL.md +310 -0
  175. package/skills/test-fixture-creation/SKILL.md +295 -0
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Coding Standards Anti-Patterns
3
+ *
4
+ * DO NOT use these patterns in production code.
5
+ * Each example violates coding standards.
6
+ */
7
+
8
+ // CS1: Multiple Responsibilities
9
+ // BAD: Function does validation, transformation, persistence, AND notification
10
+ async function processOrderViolation(order: Order) {
11
+ // Validate
12
+ if (!order.items.length) throw new Error('Empty order');
13
+
14
+ // Transform
15
+ order.total = order.items.reduce((sum, i) => sum + i.price, 0);
16
+
17
+ // Persist
18
+ await database.orders.save(order);
19
+
20
+ // Notify
21
+ await emailService.send(order.customer.email, 'Order confirmed');
22
+
23
+ // Track
24
+ await analytics.track('order_processed', order.id);
25
+
26
+ return order;
27
+ }
28
+
29
+ // CS2: Magic Values
30
+ // BAD: Hardcoded values without explanation
31
+ function calculateDiscountViolation(total: number, customerType: string): number {
32
+ if (total > 100) {
33
+ if (customerType === 'gold') {
34
+ return total * 0.2;
35
+ } else if (customerType === 'silver') {
36
+ return total * 0.1;
37
+ }
38
+ }
39
+ return total * 0.05;
40
+ }
41
+
42
+ // CS2: Hidden Mutation
43
+ // BAD: Function modifies input object without indicating it
44
+ function normalizeUserViolation(user: User) {
45
+ user.email = user.email.toLowerCase();
46
+ user.name = user.name.trim();
47
+ user.createdAt = new Date();
48
+ // Returns void but mutated input
49
+ }
50
+
51
+ // CS2: Implicit Global State
52
+ // BAD: Function depends on undeclared global
53
+ let currentTransaction: Transaction;
54
+
55
+ function processPaymentViolation(amount: number) {
56
+ // Uses global state without indicating dependency
57
+ currentTransaction.amount = amount;
58
+ currentTransaction.status = 'processing';
59
+ }
60
+
61
+ // CS2: Side Effect in Getter
62
+ // BAD: Property access triggers external action
63
+ class UserProfileViolation {
64
+ private _views = 0;
65
+
66
+ get viewCount(): number {
67
+ // Side effect: logs to analytics
68
+ analytics.track('profile_viewed', this.id);
69
+ this._views++;
70
+ return this._views;
71
+ }
72
+ }
73
+
74
+ // CS3: Silent Failure
75
+ // BAD: Catches and swallows error
76
+ async function fetchDataViolation(id: string) {
77
+ try {
78
+ return await api.getData(id);
79
+ } catch (error) {
80
+ // Silent failure - no logging, no re-throw
81
+ return null;
82
+ }
83
+ }
84
+
85
+ // CS3: Default on Error
86
+ // BAD: Returns default instead of failing
87
+ function parseConfigViolation(raw: string): Config {
88
+ try {
89
+ return JSON.parse(raw);
90
+ } catch {
91
+ // Returns invalid default, masking the error
92
+ return { apiUrl: 'http://localhost', timeout: 5000 };
93
+ }
94
+ }
95
+
96
+ // CS3: Missing Input Validation
97
+ // BAD: Public function without validation
98
+ function calculateAgeViolation(birthYear: number): number {
99
+ // No validation - negative years, future years accepted
100
+ return new Date().getFullYear() - birthYear;
101
+ }
102
+
103
+ // CS4: Unused Imports
104
+ import { unusedHelper, anotherUnused } from './utils';
105
+ import * as everythingUnused from './everything';
106
+
107
+ // CS4: Unused Variables
108
+ function processItemsViolation(items: Item[]) {
109
+ const unusedConfig = loadConfig();
110
+ const unusedHelper = createHelper();
111
+
112
+ return items.map((item) => item.name);
113
+ }
114
+
115
+ // CS4: Commented-Out Code
116
+ function handleRequestViolation(req: Request) {
117
+ // const oldWay = processOldWay(req);
118
+ // if (oldWay.success) {
119
+ // return oldWay.result;
120
+ // }
121
+
122
+ // TODO: Remove old code after migration
123
+ // const legacyResult = legacyHandler(req);
124
+ // legacyResult.transformed = true;
125
+ // return legacyResult;
126
+
127
+ return processNewWay(req);
128
+ }
129
+
130
+ // CS4: Dead Code
131
+ function processWithDeadCode(value: number): number {
132
+ if (value > 0) {
133
+ return value * 2;
134
+ }
135
+
136
+ return value;
137
+
138
+ // Dead code - unreachable
139
+ console.log('This never executes');
140
+ return value * 3;
141
+ }
142
+
143
+ // Missing Documentation on Public API
144
+ export function calculateComplexMetric(
145
+ data: MetricData[],
146
+ weights: number[],
147
+ normalize: boolean,
148
+ threshold: number
149
+ ): ComplexResult {
150
+ // No documentation for public function with many parameters
151
+ // Users have to read the code to understand behavior
152
+ return internalCalculation(data, weights, normalize, threshold);
153
+ }
154
+
155
+ // Outdated Documentation
156
+ /**
157
+ * Processes user data
158
+ * @param user - The user to process
159
+ * @deprecated Use newProcessUser instead
160
+ */
161
+ function processUserViolation(user: User, options: ProcessOptions): Result {
162
+ // Function signature changed but docs not updated
163
+ // 'options' parameter not documented
164
+ // Not actually deprecated
165
+ return doProcessing(user, options);
166
+ }
167
+
168
+ // Inconsistent Error Handling
169
+ async function fetchAllViolation() {
170
+ // Mix of error handling styles in same function
171
+ const users = await fetch('/users').catch(() => []);
172
+
173
+ let orders;
174
+ try {
175
+ orders = await fetch('/orders');
176
+ } catch (e) {
177
+ orders = [];
178
+ }
179
+
180
+ const products = await fetch('/products'); // No error handling
181
+
182
+ return { users, orders, products };
183
+ }
184
+
185
+ // Inconsistent Async Style
186
+ async function loadDataViolation() {
187
+ // Mix of .then() and await in same function
188
+ const config = await loadConfig();
189
+
190
+ return fetch(config.url)
191
+ .then((res) => res.json())
192
+ .then((data) => {
193
+ return processData(data);
194
+ });
195
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Type Safety Anti-Patterns
3
+ *
4
+ * DO NOT use these patterns in production code.
5
+ * Each example demonstrates a type safety hole.
6
+ */
7
+
8
+ // Explicit `any` - bypasses type checking entirely
9
+ function processDataUnsafe(data: any) {
10
+ // No type checking, any access is allowed
11
+ return data.deeply.nested.property.that.might.not.exist;
12
+ }
13
+
14
+ // Return type `any` - loses type information for callers
15
+ async function fetchUserUnsafe(id: string): Promise<any> {
16
+ const response = await fetch(`/api/users/${id}`);
17
+ return response.json();
18
+ }
19
+
20
+ // Variable typed as `any`
21
+ const configUnsafe: any = JSON.parse(rawConfig);
22
+ console.log(configUnsafe.setting.that.might.not.exist);
23
+
24
+ // Implicit `any` from missing parameter type (with noImplicitAny: false)
25
+ function calculateUnsafe(a, b) {
26
+ return a + b; // Could be string concatenation or number addition
27
+ }
28
+
29
+ // Non-null assertion abuse
30
+ interface User {
31
+ name: string;
32
+ profile?: {
33
+ avatar?: string;
34
+ };
35
+ }
36
+
37
+ function getAvatarUnsafe(user: User | null): string {
38
+ // BAD: Assumes user and profile exist
39
+ return user!.profile!.avatar!;
40
+ }
41
+
42
+ // Optional chaining gap - mixed `.` and `?.`
43
+ function getNameUnsafe(user: User | null): string {
44
+ // BAD: `profile` could be undefined, but we use `.avatar`
45
+ return user?.profile.avatar ?? 'default';
46
+ }
47
+
48
+ // Truthy check when 0 or "" is valid
49
+ function getCountUnsafe(items?: number): number {
50
+ // BAD: Returns 10 when items is 0
51
+ if (!items) {
52
+ return 10;
53
+ }
54
+ return items;
55
+ }
56
+
57
+ // Double assertion - almost always wrong
58
+ function convertUnsafe(value: string): number {
59
+ // BAD: Forces incompatible type conversion
60
+ return value as unknown as number;
61
+ }
62
+
63
+ // Type assertion without validation
64
+ interface Config {
65
+ apiUrl: string;
66
+ timeout: number;
67
+ }
68
+
69
+ function loadConfigUnsafe(): Config {
70
+ const raw = JSON.parse(process.env.CONFIG!);
71
+ // BAD: No validation that raw matches Config shape
72
+ return raw as Config;
73
+ }
74
+
75
+ // Widening to `any` then narrowing
76
+ function parseResponseUnsafe(response: Response): UserData {
77
+ const data = response.json() as any;
78
+ // BAD: Loses all type safety
79
+ return data as UserData;
80
+ }
81
+
82
+ // Type assertion on non-overlapping types (via unknown)
83
+ function stringToNumberUnsafe(str: string): number {
84
+ // This compiles but is nonsensical
85
+ return str as unknown as number;
86
+ }
87
+
88
+ // Ignoring null/undefined with assertion
89
+ async function getFirstUserUnsafe(): Promise<User> {
90
+ const users = await fetchUsers();
91
+ // BAD: Array could be empty
92
+ return users[0]!;
93
+ }
94
+
95
+ // Untyped catch clause
96
+ async function fetchDataUnsafe() {
97
+ try {
98
+ return await fetch('/api/data');
99
+ } catch (e) {
100
+ // BAD: `e` is `unknown` but used as Error
101
+ console.log(e.message); // TypeScript error in strict mode
102
+ }
103
+ }
104
+
105
+ // Generic constraint `any` - defeats purpose of generics
106
+ function wrapUnsafe<T extends any>(value: T): { wrapped: T } {
107
+ return { wrapped: value };
108
+ }
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Linting Recommended Patterns
3
+ *
4
+ * These examples demonstrate clean code practices.
5
+ */
6
+
7
+ // Early returns instead of deep nesting
8
+ function processOrderSafe(order: Order | null) {
9
+ if (!order) return;
10
+ if (!order.items?.length) return;
11
+
12
+ for (const item of order.items) {
13
+ if (item.quantity <= 0) continue;
14
+ if (item.price <= 0) continue;
15
+
16
+ console.log(item);
17
+ }
18
+ }
19
+
20
+ // Small, focused functions
21
+ async function handleCheckoutSafe(cart: Cart, user: User): Promise<Order> {
22
+ validateCart(cart);
23
+
24
+ const totals = calculateTotals(cart, user);
25
+ const order = createOrder(cart, user, totals);
26
+
27
+ await saveOrder(order, cart);
28
+ await sendNotifications(order, user);
29
+ await updateUserStats(user, totals.total);
30
+
31
+ return order;
32
+ }
33
+
34
+ function validateCart(cart: Cart): void {
35
+ if (!cart.items.length) throw new Error('Empty cart');
36
+
37
+ for (const item of cart.items) {
38
+ if (!item.available) throw new Error('Item unavailable');
39
+ if (item.quantity > item.stock) throw new Error('Insufficient stock');
40
+ }
41
+ }
42
+
43
+ interface Totals {
44
+ subtotal: number;
45
+ discount: number;
46
+ tax: number;
47
+ shipping: number;
48
+ total: number;
49
+ }
50
+
51
+ function calculateTotals(cart: Cart, user: User): Totals {
52
+ const subtotal = cart.items.reduce(
53
+ (sum, item) => sum + item.price * item.quantity,
54
+ 0
55
+ );
56
+ const discount = user.isPremium ? subtotal * 0.1 : 0;
57
+ const tax = (subtotal - discount) * 0.08;
58
+ const shipping = subtotal > 100 ? 0 : 10;
59
+ const total = subtotal - discount + tax + shipping;
60
+
61
+ return { subtotal, discount, tax, shipping, total };
62
+ }
63
+
64
+ // Descriptive naming
65
+ function transformProductData(rawProducts: RawProduct[]): TransformedProduct[] {
66
+ return rawProducts.map((product) => {
67
+ const discountedPrice = product.basePrice * 2;
68
+ return { ...product, discountedPrice };
69
+ });
70
+ }
71
+
72
+ // Meaningful variable names
73
+ function calculateRectangleArea(
74
+ width: number,
75
+ height: number,
76
+ margin: number,
77
+ padding: number
78
+ ): number {
79
+ const innerWidth = width - margin * 2;
80
+ const innerHeight = height - padding * 2;
81
+ const area = innerWidth * innerHeight;
82
+ return area;
83
+ }
84
+
85
+ // Accurate naming
86
+ const currentUser = getUser(); // Singular, clear
87
+ const validationResult = validate(data); // Describes what it returns
88
+ const permanentDiscountRate = 0.15; // Accurate description
89
+
90
+ // Single responsibility functions
91
+ function parseInput(input: string): ParsedData {
92
+ return JSON.parse(input);
93
+ }
94
+
95
+ function validateData(data: ParsedData): void {
96
+ if (!data.name) throw new Error('Missing name');
97
+ }
98
+
99
+ function transformData(data: ParsedData): TransformedData {
100
+ return { ...data, name: data.name.toUpperCase() };
101
+ }
102
+
103
+ function saveData(data: TransformedData): void {
104
+ localStorage.setItem('data', JSON.stringify(data));
105
+ }
106
+
107
+ function notifyDataUpdate(data: TransformedData): void {
108
+ dispatchEvent(new CustomEvent('dataUpdated', { detail: data }));
109
+ }
110
+
111
+ function processInput(input: string): TransformedData {
112
+ const parsed = parseInput(input);
113
+ validateData(parsed);
114
+ const transformed = transformData(parsed);
115
+ saveData(transformed);
116
+ notifyDataUpdate(transformed);
117
+ return transformed;
118
+ }
119
+
120
+ // Named conditions for complex logic
121
+ function canAccessResource(user: User, resource: Resource): boolean {
122
+ const isAdmin = user.role === 'admin';
123
+ const isManagerOfDepartment =
124
+ user.role === 'manager' && user.department === resource.department;
125
+ const isEmployeeWithPublicAccess =
126
+ user.role === 'employee' &&
127
+ user.department === resource.department &&
128
+ resource.accessLevel === 'public';
129
+ const hasDirectPermission = user.permissions.includes(resource.id);
130
+ const hasSharedAccess =
131
+ resource.sharedWith.includes(user.id) && !resource.revoked;
132
+
133
+ return (
134
+ isAdmin ||
135
+ isManagerOfDepartment ||
136
+ isEmployeeWithPublicAccess ||
137
+ hasDirectPermission ||
138
+ hasSharedAccess
139
+ );
140
+ }
141
+
142
+ // Extract shared logic to avoid duplication
143
+ function validateEmail(email: string): void {
144
+ if (!email) throw new Error('Email required');
145
+ if (!email.includes('@')) throw new Error('Invalid email');
146
+ if (email.length > 255) throw new Error('Email too long');
147
+ }
148
+
149
+ function createUserSafe(data: UserData) {
150
+ validateEmail(data.email);
151
+ // ...create user
152
+ }
153
+
154
+ function updateUserSafe(id: string, data: UserData) {
155
+ validateEmail(data.email);
156
+ // ...update user
157
+ }
158
+
159
+ // Consistent async style
160
+ async function fetchDataConsistent() {
161
+ const response = await fetch('/api/data');
162
+ const data = await response.json();
163
+ return processData(data);
164
+ }
165
+
166
+ async function fetchOtherDataConsistent() {
167
+ const response = await fetch('/api/other');
168
+ const data = await response.json();
169
+ return processData(data);
170
+ }
171
+
172
+ // Named constants instead of magic numbers
173
+ const SHIPPING_RATES = {
174
+ LIGHT_PER_MILE: 0.5,
175
+ LIGHT_BASE: 3.99,
176
+ MEDIUM_PER_MILE: 0.75,
177
+ MEDIUM_BASE: 7.99,
178
+ HEAVY_PER_MILE: 1.25,
179
+ HEAVY_BASE: 14.99,
180
+ } as const;
181
+
182
+ const WEIGHT_THRESHOLDS = {
183
+ LIGHT: 5,
184
+ MEDIUM: 20,
185
+ } as const;
186
+
187
+ function calculateShippingSafe(weight: number, distance: number): number {
188
+ if (weight < WEIGHT_THRESHOLDS.LIGHT) {
189
+ return distance * SHIPPING_RATES.LIGHT_PER_MILE + SHIPPING_RATES.LIGHT_BASE;
190
+ }
191
+ if (weight < WEIGHT_THRESHOLDS.MEDIUM) {
192
+ return distance * SHIPPING_RATES.MEDIUM_PER_MILE + SHIPPING_RATES.MEDIUM_BASE;
193
+ }
194
+ return distance * SHIPPING_RATES.HEAVY_PER_MILE + SHIPPING_RATES.HEAVY_BASE;
195
+ }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Security Recommended Patterns
3
+ *
4
+ * These examples demonstrate secure coding practices.
5
+ */
6
+
7
+ // A03:2021 - SQL Injection Prevention
8
+ // GOOD: Parameterized query
9
+ async function getUserByIdSafe(userId: string) {
10
+ const query = 'SELECT * FROM users WHERE id = ?';
11
+ return db.query(query, [userId]);
12
+ }
13
+
14
+ // A03:2021 - Command Injection Prevention
15
+ // GOOD: Use array-based spawn, validate input
16
+ function processFileSafe(filename: string) {
17
+ const { spawnSync } = require('child_process');
18
+
19
+ // Validate filename contains only safe characters
20
+ if (!/^[\w.-]+$/.test(filename)) {
21
+ throw new Error('Invalid filename');
22
+ }
23
+
24
+ // Use array form to prevent shell interpretation
25
+ spawnSync('grep', ['pattern', filename]);
26
+ }
27
+
28
+ // A03:2021 - XSS Prevention
29
+ // GOOD: Use textContent or proper escaping
30
+ function renderCommentSafe(comment: string) {
31
+ const element = document.getElementById('comments');
32
+ element.textContent = comment; // Safe: treated as text, not HTML
33
+ }
34
+
35
+ // Alternative: Use a sanitization library
36
+ function renderCommentWithSanitization(comment: string) {
37
+ const DOMPurify = require('dompurify');
38
+ const clean = DOMPurify.sanitize(comment);
39
+ document.getElementById('comments').innerHTML = clean;
40
+ }
41
+
42
+ // A02:2021 - Secrets Management
43
+ // GOOD: Use environment variables
44
+ const API_KEY = process.env.API_KEY;
45
+ const DB_PASSWORD = process.env.DB_PASSWORD;
46
+
47
+ // Validate secrets are present at startup
48
+ if (!API_KEY || !DB_PASSWORD) {
49
+ throw new Error('Required environment variables not set');
50
+ }
51
+
52
+ // A02:2021 - Strong Cryptography
53
+ // GOOD: bcrypt for password hashing
54
+ import * as bcrypt from 'bcrypt';
55
+
56
+ async function hashPasswordSafe(password: string): Promise<string> {
57
+ const saltRounds = 12;
58
+ return bcrypt.hash(password, saltRounds);
59
+ }
60
+
61
+ async function verifyPassword(password: string, hash: string): Promise<boolean> {
62
+ return bcrypt.compare(password, hash);
63
+ }
64
+
65
+ // A02:2021 - Secure Random
66
+ // GOOD: crypto.randomBytes for security-sensitive operations
67
+ import * as crypto from 'crypto';
68
+
69
+ function generateTokenSafe(): string {
70
+ return crypto.randomBytes(32).toString('hex');
71
+ }
72
+
73
+ // A01:2021 - Authorization Check
74
+ // GOOD: Verify ownership before returning data
75
+ async function getDocumentSafe(documentId: string, userId: string) {
76
+ const document = await db.documents.findById(documentId);
77
+
78
+ if (!document) {
79
+ throw new NotFoundError('Document not found');
80
+ }
81
+
82
+ if (document.ownerId !== userId) {
83
+ throw new ForbiddenError('Access denied');
84
+ }
85
+
86
+ return document;
87
+ }
88
+
89
+ // A01:2021 - Path Traversal Prevention
90
+ // GOOD: Validate and normalize path
91
+ import * as path from 'path';
92
+
93
+ function readFileSafe(userPath: string) {
94
+ const uploadsDir = path.resolve('./uploads');
95
+ const requestedPath = path.resolve(uploadsDir, userPath);
96
+
97
+ // Ensure path is within uploads directory
98
+ if (!requestedPath.startsWith(uploadsDir)) {
99
+ throw new Error('Invalid path');
100
+ }
101
+
102
+ const fs = require('fs');
103
+ return fs.readFileSync(requestedPath);
104
+ }
105
+
106
+ // A07:2021 - Session Regeneration
107
+ // GOOD: Regenerate session after authentication
108
+ async function loginSafe(req: Request, res: Response) {
109
+ const user = await authenticate(req.body);
110
+
111
+ // Regenerate session to prevent fixation
112
+ req.session.regenerate((err) => {
113
+ if (err) throw err;
114
+ req.session.userId = user.id;
115
+ res.json({ success: true });
116
+ });
117
+ }
118
+
119
+ // A09:2021 - Safe Logging
120
+ // GOOD: Never log sensitive data
121
+ function authenticateSafe(username: string, password: string) {
122
+ console.log(`Login attempt for user: ${username}`);
123
+ // Password is NOT logged
124
+ const result = verifyCredentials(username, password);
125
+ console.log(`Login result for ${username}: ${result ? 'success' : 'failure'}`);
126
+ }
127
+
128
+ // A10:2021 - SSRF Prevention
129
+ // GOOD: Allowlist for external URLs
130
+ const ALLOWED_HOSTS = ['api.example.com', 'cdn.example.com'];
131
+
132
+ async function fetchUrlSafe(url: string) {
133
+ const parsedUrl = new URL(url);
134
+
135
+ if (!ALLOWED_HOSTS.includes(parsedUrl.hostname)) {
136
+ throw new Error('URL not in allowlist');
137
+ }
138
+
139
+ const response = await fetch(url);
140
+ return response.text();
141
+ }
142
+
143
+ // A05:2021 - Production Error Handling
144
+ // GOOD: Generic error message, log details server-side
145
+ function handleErrorSafe(error: Error, res: Response) {
146
+ // Log full details server-side
147
+ console.error('Internal error:', error);
148
+
149
+ // Return generic message to client
150
+ res.status(500).json({
151
+ error: 'An internal error occurred',
152
+ requestId: generateRequestId(), // For support correlation
153
+ });
154
+ }