docs: tsx,powertransfertab,packages (3 files)

This commit is contained in:
2025-12-26 01:15:02 +00:00
parent 08f3856743
commit bc84907b84
3 changed files with 163 additions and 9 deletions

View File

@@ -213,14 +213,103 @@ private handleMessage(data: string): void {
- Could modify Object prototype affecting all objects in process
- Pattern: CVE-2019-10744 (lodash prototype pollution)
**Recommendation**:
**🏰 Fort Knox Remediation**:
```typescript
import { safeJsonParse } from './security-utils'
/**
* Fort Knox JSON Parser
* - Blocks prototype pollution
* - Enforces depth limits
* - Validates structure against schema
* - Sanitizes string content
*/
class SecureJsonParser {
private static readonly MAX_DEPTH = 10
private static readonly MAX_STRING_LENGTH = 1_000_000 // 1MB
private static readonly MAX_ARRAY_LENGTH = 10_000
private static readonly MAX_OBJECT_KEYS = 1000
private static readonly FORBIDDEN_KEYS = new Set([
'__proto__',
'constructor',
'prototype',
'__defineGetter__',
'__defineSetter__',
'__lookupGetter__',
'__lookupSetter__',
])
static parse<T>(json: string, schema?: ZodSchema<T>): T {
// 1. Length check
if (json.length > this.MAX_STRING_LENGTH) {
throw DBALError.validationError('JSON input too large', [
{ field: 'json', error: `Max ${this.MAX_STRING_LENGTH} bytes` }
])
}
// 2. Parse with reviver for prototype protection
let depth = 0
const parsed = JSON.parse(json, (key, value) => {
// Block dangerous keys
if (this.FORBIDDEN_KEYS.has(key)) {
throw DBALError.maliciousCode(`Forbidden key in JSON: ${key}`)
}
// Track and limit depth
if (typeof value === 'object' && value !== null) {
depth++
if (depth > this.MAX_DEPTH) {
throw DBALError.validationError('JSON nesting too deep')
}
// Limit object keys
if (!Array.isArray(value) && Object.keys(value).length > this.MAX_OBJECT_KEYS) {
throw DBALError.validationError('Too many object keys')
}
// Limit array length
if (Array.isArray(value) && value.length > this.MAX_ARRAY_LENGTH) {
throw DBALError.validationError('Array too large')
}
}
return value
})
// 3. Validate against schema if provided
if (schema) {
const result = schema.safeParse(parsed)
if (!result.success) {
throw DBALError.validationError('Schema validation failed',
result.error.issues.map(i => ({ field: i.path.join('.'), error: i.message }))
)
}
return result.data
}
// 4. Deep freeze to prevent mutation
return this.deepFreeze(parsed)
}
private static deepFreeze<T>(obj: T): T {
if (obj === null || typeof obj !== 'object') return obj
Object.freeze(obj)
Object.getOwnPropertyNames(obj).forEach(prop => {
const value = (obj as any)[prop]
if (value !== null && typeof value === 'object' && !Object.isFrozen(value)) {
this.deepFreeze(value)
}
})
return obj
}
}
const response = safeJsonParse(data, {
protoAction: 'remove',
constructorAction: 'remove'
})
// Usage in WebSocket bridge
private handleMessage(data: string): void {
const response = SecureJsonParser.parse(data, RPCResponseSchema)
// response is now validated, frozen, and safe
}
```
---

View File

@@ -31,11 +31,14 @@ const renderComponent = (overrides?: { onInitiate?: () => void }) => {
return { onInitiateTransfer }
}
const mockFetch = (payload: any) =>
vi.stubGlobal('fetch', async () => ({
const mockFetch = (payload: any) => {
const fetchMock = vi.fn(async () => ({
ok: true,
json: async () => payload,
}))
vi.stubGlobal('fetch', fetchMock)
return fetchMock
}
afterEach(() => {
vi.restoreAllMocks()
@@ -71,7 +74,7 @@ describe('PowerTransferTab', () => {
const { onInitiateTransfer } = renderComponent()
await waitFor(() => expect(screen.getByText(/Select User/i)).toBeInTheDocument())
await screen.findByText(/Select User/i)
fireEvent.click(screen.getByText('target-god'))
const actionButton = screen.getByRole('button', { name: /Initiate Power Transfer/i })

View File

@@ -10,6 +10,26 @@
"id": "cat_growth",
"name": "Growth Ops",
"description": "Retention, onboarding, and community tactics"
},
{
"id": "cat_design",
"name": "Design Systems",
"description": "Tokens, theming, and UI governance"
},
{
"id": "cat_ai",
"name": "AI Tooling",
"description": "Prompt playbooks, evals, and guardrails"
},
{
"id": "cat_ops",
"name": "Community Ops",
"description": "Moderation workflows and engagement rituals"
},
{
"id": "cat_hiring",
"name": "Hiring & Ops",
"description": "Team growth, planning, and operating cadence"
}
],
"threads": [
@@ -26,6 +46,48 @@
"categoryId": "cat_launch",
"replyCount": 18,
"likeCount": 64
},
{
"id": "thread_3",
"title": "Building a tokenized design system",
"categoryId": "cat_design",
"replyCount": 27,
"likeCount": 88
},
{
"id": "thread_4",
"title": "How to measure community health",
"categoryId": "cat_ops",
"replyCount": 33,
"likeCount": 96
},
{
"id": "thread_5",
"title": "Prompt playbooks for onboarding",
"categoryId": "cat_ai",
"replyCount": 21,
"likeCount": 54
},
{
"id": "thread_6",
"title": "Moderation handoff checklists",
"categoryId": "cat_ops",
"replyCount": 19,
"likeCount": 41
},
{
"id": "thread_7",
"title": "Hiring your first community lead",
"categoryId": "cat_hiring",
"replyCount": 15,
"likeCount": 33
},
{
"id": "thread_8",
"title": "Feature flag cadence for launches",
"categoryId": "cat_launch",
"replyCount": 12,
"likeCount": 29
}
]
}