fix: harden unicode safety checks

This commit is contained in:
Affaan Mustafa
2026-03-29 08:59:06 -04:00
parent dd675d4258
commit 866d9ebb53
239 changed files with 3780 additions and 3962 deletions

View File

@@ -103,12 +103,12 @@ c) 影響度別に優先順位付け
**パターン 1: 型推論の失敗**
```typescript
// エラー: Parameter 'x' implicitly has an 'any' type
// FAIL: エラー: Parameter 'x' implicitly has an 'any' type
function add(x, y) {
return x + y
}
// 修正: 型アノテーションを追加
// PASS: 修正: 型アノテーションを追加
function add(x: number, y: number): number {
return x + y
}
@@ -116,25 +116,25 @@ function add(x: number, y: number): number {
**パターン 2: Null/Undefinedエラー**
```typescript
// エラー: Object is possibly 'undefined'
// FAIL: エラー: Object is possibly 'undefined'
const name = user.name.toUpperCase()
// 修正: オプショナルチェーン
// PASS: 修正: オプショナルチェーン
const name = user?.name?.toUpperCase()
// または: Nullチェック
// PASS: または: Nullチェック
const name = user && user.name ? user.name.toUpperCase() : ''
```
**パターン 3: プロパティの欠落**
```typescript
// エラー: Property 'age' does not exist on type 'User'
// FAIL: エラー: Property 'age' does not exist on type 'User'
interface User {
name: string
}
const user: User = { name: 'John', age: 30 }
// 修正: インターフェースにプロパティを追加
// PASS: 修正: インターフェースにプロパティを追加
interface User {
name: string
age?: number // 常に存在しない場合はオプショナル
@@ -143,10 +143,10 @@ interface User {
**パターン 4: インポートエラー**
```typescript
// エラー: Cannot find module '@/lib/utils'
// FAIL: エラー: Cannot find module '@/lib/utils'
import { formatDate } from '@/lib/utils'
// 修正1: tsconfigのパスが正しいか確認
// PASS: 修正1: tsconfigのパスが正しいか確認
{
"compilerOptions": {
"paths": {
@@ -155,38 +155,38 @@ import { formatDate } from '@/lib/utils'
}
}
// 修正2: 相対インポートを使用
// PASS: 修正2: 相対インポートを使用
import { formatDate } from '../lib/utils'
// 修正3: 欠落しているパッケージをインストール
// PASS: 修正3: 欠落しているパッケージをインストール
npm install @/lib/utils
```
**パターン 5: 型の不一致**
```typescript
// エラー: Type 'string' is not assignable to type 'number'
// FAIL: エラー: Type 'string' is not assignable to type 'number'
const age: number = "30"
// 修正: 文字列を数値にパース
// PASS: 修正: 文字列を数値にパース
const age: number = parseInt("30", 10)
// または: 型を変更
// PASS: または: 型を変更
const age: string = "30"
```
**パターン 6: ジェネリック制約**
```typescript
// エラー: Type 'T' is not assignable to type 'string'
// FAIL: エラー: Type 'T' is not assignable to type 'string'
function getLength<T>(item: T): number {
return item.length
}
// 修正: 制約を追加
// PASS: 修正: 制約を追加
function getLength<T extends { length: number }>(item: T): number {
return item.length
}
// または: より具体的な制約
// PASS: または: より具体的な制約
function getLength<T extends string | any[]>(item: T): number {
return item.length
}
@@ -194,14 +194,14 @@ function getLength<T extends string | any[]>(item: T): number {
**パターン 7: React Hookエラー**
```typescript
// エラー: React Hook "useState" cannot be called in a function
// FAIL: エラー: React Hook "useState" cannot be called in a function
function MyComponent() {
if (condition) {
const [state, setState] = useState(0) // エラー!
}
}
// 修正: フックをトップレベルに移動
// PASS: 修正: フックをトップレベルに移動
function MyComponent() {
const [state, setState] = useState(0)
@@ -215,12 +215,12 @@ function MyComponent() {
**パターン 8: Async/Awaitエラー**
```typescript
// エラー: 'await' expressions are only allowed within async functions
// FAIL: エラー: 'await' expressions are only allowed within async functions
function fetchData() {
const data = await fetch('/api/data')
}
// 修正: asyncキーワードを追加
// PASS: 修正: asyncキーワードを追加
async function fetchData() {
const data = await fetch('/api/data')
}
@@ -228,14 +228,14 @@ async function fetchData() {
**パターン 9: モジュールが見つからない**
```typescript
// エラー: Cannot find module 'react' or its corresponding type declarations
// FAIL: エラー: Cannot find module 'react' or its corresponding type declarations
import React from 'react'
// 修正: 依存関係をインストール
// PASS: 修正: 依存関係をインストール
npm install react
npm install --save-dev @types/react
// 確認: package.jsonに依存関係があることを確認
// PASS: 確認: package.jsonに依存関係があることを確認
{
"dependencies": {
"react": "^19.0.0"
@@ -248,18 +248,18 @@ npm install --save-dev @types/react
**パターン 10: Next.js固有のエラー**
```typescript
// エラー: Fast Refresh had to perform a full reload
// FAIL: エラー: Fast Refresh had to perform a full reload
// 通常、コンポーネント以外のエクスポートが原因
// 修正: エクスポートを分離
// 間違い: file.tsx
// PASS: 修正: エクスポートを分離
// FAIL: 間違い: file.tsx
export const MyComponent = () => <div />
export const someConstant = 42 // フルリロードの原因
// 正しい: component.tsx
// PASS: 正しい: component.tsx
export const MyComponent = () => <div />
// 正しい: constants.ts
// PASS: 正しい: constants.ts
export const someConstant = 42
```
@@ -267,7 +267,7 @@ export const someConstant = 42
### Next.js 15 + React 19の互換性
```typescript
// エラー: React 19の型変更
// FAIL: エラー: React 19の型変更
import { FC } from 'react'
interface Props {
@@ -278,7 +278,7 @@ const Component: FC<Props> = ({ children }) => {
return <div>{children}</div>
}
// 修正: React 19ではFCは不要
// PASS: 修正: React 19ではFCは不要
interface Props {
children: React.ReactNode
}
@@ -290,12 +290,12 @@ const Component = ({ children }: Props) => {
### Supabaseクライアントの型
```typescript
// エラー: Type 'any' not assignable
// FAIL: エラー: Type 'any' not assignable
const { data } = await supabase
.from('markets')
.select('*')
// 修正: 型アノテーションを追加
// PASS: 修正: 型アノテーションを追加
interface Market {
id: string
name: string
@@ -310,10 +310,10 @@ const { data } = await supabase
### Redis Stackの型
```typescript
// エラー: Property 'ft' does not exist on type 'RedisClientType'
// FAIL: エラー: Property 'ft' does not exist on type 'RedisClientType'
const results = await client.ft.search('idx:markets', query)
// 修正: 適切なRedis Stackの型を使用
// PASS: 修正: 適切なRedis Stackの型を使用
import { createClient } from 'redis'
const client = createClient({
@@ -328,10 +328,10 @@ const results = await client.ft.search('idx:markets', query)
### Solana Web3.jsの型
```typescript
// エラー: Argument of type 'string' not assignable to 'PublicKey'
// FAIL: エラー: Argument of type 'string' not assignable to 'PublicKey'
const publicKey = wallet.address
// 修正: PublicKeyコンストラクタを使用
// PASS: 修正: PublicKeyコンストラクタを使用
import { PublicKey } from '@solana/web3.js'
const publicKey = new PublicKey(wallet.address)
```
@@ -341,34 +341,34 @@ const publicKey = new PublicKey(wallet.address)
**重要: できる限り最小限の変更を行う**
### すべきこと:
欠落している型アノテーションを追加
必要な箇所にnullチェックを追加
インポート/エクスポートを修正
欠落している依存関係を追加
型定義を更新
設定ファイルを修正
PASS: 欠落している型アノテーションを追加
PASS: 必要な箇所にnullチェックを追加
PASS: インポート/エクスポートを修正
PASS: 欠落している依存関係を追加
PASS: 型定義を更新
PASS: 設定ファイルを修正
### してはいけないこと:
関連のないコードをリファクタリング
アーキテクチャを変更
変数/関数の名前を変更(エラーの原因でない限り)
新機能を追加
ロジックフローを変更(エラー修正以外)
パフォーマンスを最適化
コードスタイルを改善
FAIL: 関連のないコードをリファクタリング
FAIL: アーキテクチャを変更
FAIL: 変数/関数の名前を変更(エラーの原因でない限り)
FAIL: 新機能を追加
FAIL: ロジックフローを変更(エラー修正以外)
FAIL: パフォーマンスを最適化
FAIL: コードスタイルを改善
**最小差分の例:**
```typescript
// ファイルは200行あり、45行目にエラーがある
// 間違い: ファイル全体をリファクタリング
// FAIL: 間違い: ファイル全体をリファクタリング
// - 変数の名前変更
// - 関数の抽出
// - パターンの変更
// 結果: 50行変更
// 正しい: エラーのみを修正
// PASS: 正しい: エラーのみを修正
// - 45行目に型アテーションを追加
// 結果: 1行変更
@@ -376,12 +376,12 @@ function processData(data) { // 45行目 - エラー: 'data' implicitly has 'any
return data.map(item => item.value)
}
// 最小限の修正:
// PASS: 最小限の修正:
function processData(data: any[]) { // この行のみを変更
return data.map(item => item.value)
}
// より良い最小限の修正(型が既知の場合):
// PASS: より良い最小限の修正(型が既知の場合):
function processData(data: Array<{ value: number }>) {
return data.map(item => item.value)
}
@@ -396,7 +396,7 @@ function processData(data: Array<{ value: number }>) {
**ビルド対象:** Next.jsプロダクション / TypeScriptチェック / ESLint
**初期エラー数:** X
**修正済みエラー数:** Y
**ビルドステータス:** 成功 / 失敗
**ビルドステータス:** PASS: 成功 / FAIL: 失敗
## 修正済みエラー
@@ -430,17 +430,17 @@ Parameter 'market' implicitly has an 'any' type.
## 検証手順
1. TypeScriptチェック成功: `npx tsc --noEmit`
2. Next.jsビルド成功: `npm run build`
3. ESLintチェック成功: `npx eslint .`
4. 新しいエラーが導入されていない
5. 開発サーバー起動: `npm run dev`
1. PASS: TypeScriptチェック成功: `npx tsc --noEmit`
2. PASS: Next.jsビルド成功: `npm run build`
3. PASS: ESLintチェック成功: `npx eslint .`
4. PASS: 新しいエラーが導入されていない
5. PASS: 開発サーバー起動: `npm run dev`
## まとめ
- 解決されたエラー総数: X
- 変更行数総数: Y
- ビルドステータス: 成功
- ビルドステータス: PASS: 成功
- 修正時間: Z 分
- ブロッキング問題: 0 件残存
@@ -470,19 +470,19 @@ Parameter 'market' implicitly has an 'any' type.
## ビルドエラーの優先度レベル
### 🔴 クリティカル(即座に修正)
### クリティカル(即座に修正)
- ビルドが完全に壊れている
- 開発サーバーが起動しない
- プロダクションデプロイがブロックされている
- 複数のファイルが失敗している
### 🟡 高(早急に修正)
### 高(早急に修正)
- 単一ファイルの失敗
- 新しいコードの型エラー
- インポートエラー
- 重要でないビルド警告
### 🟢 中(可能な時に修正)
### 中(可能な時に修正)
- リンター警告
- 非推奨APIの使用
- 非厳格な型の問題
@@ -521,13 +521,13 @@ npm install
## 成功指標
ビルドエラー解決後:
- `npx tsc --noEmit` が終了コード0で終了
- `npm run build` が正常に完了
- 新しいエラーが導入されていない
- 最小限の行数変更影響を受けたファイルの5%未満)
- ビルド時間が大幅に増加していない
- 開発サーバーがエラーなく動作
- テストが依然として成功
- PASS: `npx tsc --noEmit` が終了コード0で終了
- PASS: `npm run build` が正常に完了
- PASS: 新しいエラーが導入されていない
- PASS: 最小限の行数変更影響を受けたファイルの5%未満)
- PASS: ビルド時間が大幅に増加していない
- PASS: 開発サーバーがエラーなく動作
- PASS: テストが依然として成功
---

View File

@@ -81,15 +81,15 @@ model: opus
問題: APIキーがソースコードに公開されている
修正: 環境変数に移動
const apiKey = "sk-abc123"; // Bad
const apiKey = "sk-abc123"; // FAIL: Bad
const apiKey = process.env.API_KEY; // ✓ Good
```
## 承認基準
- 承認: CRITICALまたはHIGH問題なし
- ⚠️ 警告: MEDIUM問題のみ注意してマージ可能
- ブロック: CRITICALまたはHIGH問題が見つかった
- PASS: 承認: CRITICALまたはHIGH問題なし
- WARNING: 警告: MEDIUM問題のみ注意してマージ可能
- FAIL: ブロック: CRITICALまたはHIGH問題が見つかった
## プロジェクト固有のガイドライン(例)

View File

@@ -112,14 +112,14 @@ c) データ保護
**影響:** 大きなテーブルで100〜1000倍高速なクエリ
```sql
-- 悪い: 外部キーにインデックスがない
-- FAIL: 悪い: 外部キーにインデックスがない
CREATE TABLE orders (
id bigint PRIMARY KEY,
customer_id bigint REFERENCES customers(id)
-- インデックスが欠落!
);
-- 良い: 外部キーにインデックス
-- PASS: 良い: 外部キーにインデックス
CREATE TABLE orders (
id bigint PRIMARY KEY,
customer_id bigint REFERENCES customers(id)
@@ -137,11 +137,11 @@ CREATE INDEX orders_customer_id_idx ON orders (customer_id);
| **Hash** | 等価のみ | `=`B-treeより若干高速 |
```sql
-- 悪い: JSONB包含のためのB-tree
-- FAIL: 悪い: JSONB包含のためのB-tree
CREATE INDEX products_attrs_idx ON products (attributes);
SELECT * FROM products WHERE attributes @> '{"color": "red"}';
-- 良い: JSONBのためのGIN
-- PASS: 良い: JSONBのためのGIN
CREATE INDEX products_attrs_idx ON products USING gin (attributes);
```
@@ -150,11 +150,11 @@ CREATE INDEX products_attrs_idx ON products USING gin (attributes);
**影響:** 複数列クエリで5〜10倍高速
```sql
-- 悪い: 個別のインデックス
-- FAIL: 悪い: 個別のインデックス
CREATE INDEX orders_status_idx ON orders (status);
CREATE INDEX orders_created_idx ON orders (created_at);
-- 良い: 複合インデックス(等価列を最初に、次に範囲)
-- PASS: 良い: 複合インデックス(等価列を最初に、次に範囲)
CREATE INDEX orders_status_created_idx ON orders (status, created_at);
```
@@ -170,11 +170,11 @@ CREATE INDEX orders_status_created_idx ON orders (status, created_at);
**影響:** テーブルルックアップを回避することで2〜5倍高速なクエリ
```sql
-- 悪い: テーブルからnameを取得する必要がある
-- FAIL: 悪い: テーブルからnameを取得する必要がある
CREATE INDEX users_email_idx ON users (email);
SELECT email, name FROM users WHERE email = 'user@example.com';
-- 良い: すべての列がインデックスに含まれる
-- PASS: 良い: すべての列がインデックスに含まれる
CREATE INDEX users_email_idx ON users (email) INCLUDE (name, created_at);
```
@@ -183,10 +183,10 @@ CREATE INDEX users_email_idx ON users (email) INCLUDE (name, created_at);
**影響:** 5〜20倍小さいインデックス、高速な書き込みとクエリ
```sql
-- 悪い: 完全なインデックスには削除された行が含まれる
-- FAIL: 悪い: 完全なインデックスには削除された行が含まれる
CREATE INDEX users_email_idx ON users (email);
-- 良い: 部分インデックスは削除された行を除外
-- PASS: 良い: 部分インデックスは削除された行を除外
CREATE INDEX users_active_email_idx ON users (email) WHERE deleted_at IS NULL;
```
@@ -202,7 +202,7 @@ CREATE INDEX users_active_email_idx ON users (email) WHERE deleted_at IS NULL;
### 1. データ型の選択
```sql
-- 悪い: 不適切な型選択
-- FAIL: 悪い: 不適切な型選択
CREATE TABLE users (
id int, -- 21億でオーバーフロー
email varchar(255), -- 人為的な制限
@@ -211,7 +211,7 @@ CREATE TABLE users (
balance float -- 精度の損失
);
-- 良い: 適切な型
-- PASS: 良い: 適切な型
CREATE TABLE users (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email text NOT NULL,
@@ -224,18 +224,18 @@ CREATE TABLE users (
### 2. 主キー戦略
```sql
-- 単一データベース: IDENTITYデフォルト、推奨
-- PASS: 単一データベース: IDENTITYデフォルト、推奨
CREATE TABLE users (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY
);
-- 分散システム: UUIDv7時間順
-- PASS: 分散システム: UUIDv7時間順
CREATE EXTENSION IF NOT EXISTS pg_uuidv7;
CREATE TABLE orders (
id uuid DEFAULT uuid_generate_v7() PRIMARY KEY
);
-- 避ける: ランダムUUIDはインデックスの断片化を引き起こす
-- FAIL: 避ける: ランダムUUIDはインデックスの断片化を引き起こす
CREATE TABLE events (
id uuid DEFAULT gen_random_uuid() PRIMARY KEY -- 断片化した挿入!
);
@@ -246,7 +246,7 @@ CREATE TABLE events (
**使用する場合:** テーブル > 1億行、時系列データ、古いデータを削除する必要がある
```sql
-- 良い: 月ごとにパーティション化
-- PASS: 良い: 月ごとにパーティション化
CREATE TABLE events (
id bigint GENERATED ALWAYS AS IDENTITY,
created_at timestamptz NOT NULL,
@@ -266,11 +266,11 @@ DROP TABLE events_2023_01; -- 数時間かかるDELETEではなく即座に
### 4. 小文字の識別子を使用
```sql
-- 悪い: 引用符付きの混合ケースは至る所で引用符が必要
-- FAIL: 悪い: 引用符付きの混合ケースは至る所で引用符が必要
CREATE TABLE "Users" ("userId" bigint, "firstName" text);
SELECT "firstName" FROM "Users"; -- 引用符が必須!
-- 良い: 小文字は引用符なしで機能
-- PASS: 良い: 小文字は引用符なしで機能
CREATE TABLE users (user_id bigint, first_name text);
SELECT first_name FROM users;
```
@@ -284,11 +284,11 @@ SELECT first_name FROM users;
**影響:** 重要 - データベースで強制されるテナント分離
```sql
-- 悪い: アプリケーションのみのフィルタリング
-- FAIL: 悪い: アプリケーションのみのフィルタリング
SELECT * FROM orders WHERE user_id = $current_user_id;
-- バグはすべての注文が露出することを意味する!
-- 良い: データベースで強制されるRLS
-- PASS: 良い: データベースで強制されるRLS
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
ALTER TABLE orders FORCE ROW LEVEL SECURITY;
@@ -308,11 +308,11 @@ CREATE POLICY orders_user_policy ON orders
**影響:** 5〜10倍高速なRLSクエリ
```sql
-- 悪い: 関数が行ごとに呼び出される
-- FAIL: 悪い: 関数が行ごとに呼び出される
CREATE POLICY orders_policy ON orders
USING (auth.uid() = user_id); -- 100万行に対して100万回呼び出される
-- 良い: SELECTでラップキャッシュされ、一度だけ呼び出される
-- PASS: 良い: SELECTでラップキャッシュされ、一度だけ呼び出される
CREATE POLICY orders_policy ON orders
USING ((SELECT auth.uid()) = user_id); -- 100倍高速
@@ -323,10 +323,10 @@ CREATE INDEX orders_user_id_idx ON orders (user_id);
### 3. 最小権限アクセス
```sql
-- 悪い: 過度に許可的
-- FAIL: 悪い: 過度に許可的
GRANT ALL PRIVILEGES ON ALL TABLES TO app_user;
-- 良い: 最小限の権限
-- PASS: 良い: 最小限の権限
CREATE ROLE app_readonly NOLOGIN;
GRANT USAGE ON SCHEMA public TO app_readonly;
GRANT SELECT ON public.products, public.categories TO app_readonly;
@@ -378,14 +378,14 @@ SELECT pg_reload_conf();
### 1. トランザクションを短く保つ
```sql
-- 悪い: 外部APIコール中にロックを保持
-- FAIL: 悪い: 外部APIコール中にロックを保持
BEGIN;
SELECT * FROM orders WHERE id = 1 FOR UPDATE;
-- HTTPコールに5秒かかる...
UPDATE orders SET status = 'paid' WHERE id = 1;
COMMIT;
-- 良い: 最小限のロック期間
-- PASS: 良い: 最小限のロック期間
-- トランザクション外で最初にAPIコールを実行
BEGIN;
UPDATE orders SET status = 'paid', payment_id = $1
@@ -397,12 +397,12 @@ COMMIT; -- ミリ秒でロックを保持
### 2. デッドロックを防ぐ
```sql
-- 悪い: 一貫性のないロック順序がデッドロックを引き起こす
-- FAIL: 悪い: 一貫性のないロック順序がデッドロックを引き起こす
-- トランザクションA: 行1をロック、次に行2
-- トランザクションB: 行2をロック、次に行1
-- デッドロック!
-- 良い: 一貫したロック順序
-- PASS: 良い: 一貫したロック順序
BEGIN;
SELECT * FROM accounts WHERE id IN (1, 2) ORDER BY id FOR UPDATE;
-- これで両方の行がロックされ、任意の順序で更新可能
@@ -416,10 +416,10 @@ COMMIT;
**影響:** ワーカーキューで10倍のスループット
```sql
-- 悪い: ワーカーが互いを待つ
-- FAIL: 悪い: ワーカーが互いを待つ
SELECT * FROM jobs WHERE status = 'pending' LIMIT 1 FOR UPDATE;
-- 良い: ワーカーはロックされた行をスキップ
-- PASS: 良い: ワーカーはロックされた行をスキップ
UPDATE jobs
SET status = 'processing', worker_id = $1, started_at = now()
WHERE id = (
@@ -441,36 +441,36 @@ RETURNING *;
**影響:** バルク挿入が10〜50倍高速
```sql
-- 悪い: 個別の挿入
-- FAIL: 悪い: 個別の挿入
INSERT INTO events (user_id, action) VALUES (1, 'click');
INSERT INTO events (user_id, action) VALUES (2, 'view');
-- 1000回のラウンドトリップ
-- 良い: バッチ挿入
-- PASS: 良い: バッチ挿入
INSERT INTO events (user_id, action) VALUES
(1, 'click'),
(2, 'view'),
(3, 'click');
-- 1回のラウンドトリップ
-- 最良: 大きなデータセットにはCOPY
-- PASS: 最良: 大きなデータセットにはCOPY
COPY events (user_id, action) FROM '/path/to/data.csv' WITH (FORMAT csv);
```
### 2. N+1クエリの排除
```sql
-- 悪い: N+1パターン
-- FAIL: 悪い: N+1パターン
SELECT id FROM users WHERE active = true; -- 100件のIDを返す
-- 次に100回のクエリ:
SELECT * FROM orders WHERE user_id = 1;
SELECT * FROM orders WHERE user_id = 2;
-- ... 98回以上
-- 良い: ANYを使用した単一クエリ
-- PASS: 良い: ANYを使用した単一クエリ
SELECT * FROM orders WHERE user_id = ANY(ARRAY[1, 2, 3, ...]);
-- 良い: JOIN
-- PASS: 良い: JOIN
SELECT u.id, u.name, o.*
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
@@ -482,11 +482,11 @@ WHERE u.active = true;
**影響:** ページの深さに関係なく一貫したO(1)パフォーマンス
```sql
-- 悪い: OFFSETは深さとともに遅くなる
-- FAIL: 悪い: OFFSETは深さとともに遅くなる
SELECT * FROM products ORDER BY id LIMIT 20 OFFSET 199980;
-- 200,000行をスキャン
-- 良い: カーソルベース(常に高速)
-- PASS: 良い: カーソルベース(常に高速)
SELECT * FROM products WHERE id > 199980 ORDER BY id LIMIT 20;
-- インデックスを使用、O(1)
```
@@ -494,11 +494,11 @@ SELECT * FROM products WHERE id > 199980 ORDER BY id LIMIT 20;
### 4. 挿入または更新のためのUPSERT
```sql
-- 悪い: 競合状態
-- FAIL: 悪い: 競合状態
SELECT * FROM settings WHERE user_id = 123 AND key = 'theme';
-- 両方のスレッドが何も見つけず、両方が挿入、一方が失敗
-- 良い: アトミックなUPSERT
-- PASS: 良い: アトミックなUPSERT
INSERT INTO settings (user_id, key, value)
VALUES (123, 'theme', 'dark')
ON CONFLICT (user_id, key)
@@ -605,27 +605,27 @@ ORDER BY rank DESC;
## フラグを立てるべきアンチパターン
### クエリアンチパターン
### FAIL: クエリアンチパターン
- 本番コードでの`SELECT *`
- WHERE/JOIN列にインデックスがない
- 大きなテーブルでのOFFSETページネーション
- N+1クエリパターン
- パラメータ化されていないクエリSQLインジェクションリスク
### スキーマアンチパターン
### FAIL: スキーマアンチパターン
- IDに`int``bigint`を使用)
- 理由なく`varchar(255)``text`を使用)
- タイムゾーンなしの`timestamp``timestamptz`を使用)
- 主キーとしてのランダムUUIDUUIDv7またはIDENTITYを使用
- 引用符を必要とする混合ケースの識別子
### セキュリティアンチパターン
### FAIL: セキュリティアンチパターン
- アプリケーションユーザーへの`GRANT ALL`
- マルチテナントテーブルでRLSが欠落
- 行ごとに関数を呼び出すRLSポリシーSELECTでラップされていない
- RLSポリシー列にインデックスがない
### 接続アンチパターン
### FAIL: 接続アンチパターン
- 接続プーリングなし
- アイドルタイムアウトなし
- トランザクションモードプーリングでのプリペアドステートメント

View File

@@ -386,7 +386,7 @@ function extractJSDoc(pattern: string) {
- [x] 古い参照なし
### 影響
🟢 低 - ドキュメントのみ、コード変更なし
低 - ドキュメントのみ、コード変更なし
完全なアーキテクチャ概要についてはdocs/CODEMAPS/INDEX.mdを参照してください。
```

View File

@@ -428,28 +428,28 @@ test('market search with complex query', async ({ page }) => {
**1. 競合状態**
```typescript
// 不安定: 要素が準備完了であると仮定しない
// FAIL: 不安定: 要素が準備完了であると仮定しない
await page.click('[data-testid="button"]')
// 安定: 要素が準備完了になるのを待つ
// PASS: 安定: 要素が準備完了になるのを待つ
await page.locator('[data-testid="button"]').click() // 組み込みの自動待機
```
**2. ネットワークタイミング**
```typescript
// 不安定: 任意のタイムアウト
// FAIL: 不安定: 任意のタイムアウト
await page.waitForTimeout(5000)
// 安定: 特定の条件を待つ
// PASS: 安定: 特定の条件を待つ
await page.waitForResponse(resp => resp.url().includes('/api/markets'))
```
**3. アニメーションタイミング**
```typescript
// 不安定: アニメーション中にクリック
// FAIL: 不安定: アニメーション中にクリック
await page.click('[data-testid="menu-item"]')
// 安定: アニメーションが完了するのを待つ
// PASS: 安定: アニメーションが完了するのを待つ
await page.locator('[data-testid="menu-item"]').waitFor({ state: 'visible' })
await page.waitForLoadState('networkidle')
await page.click('[data-testid="menu-item"]')
@@ -548,7 +548,7 @@ jobs:
**日付:** YYYY-MM-DD HH:MM
**期間:** Xm Ys
**ステータス:** 成功 / 失敗
**ステータス:** PASS: 成功 / FAIL: 失敗
## まとめ
@@ -561,20 +561,20 @@ jobs:
## スイート別テスト結果
### Markets - ブラウズと検索
- user can browse markets (2.3s)
- semantic search returns relevant results (1.8s)
- search handles no results (1.2s)
- search with special characters (0.9s)
- PASS: user can browse markets (2.3s)
- PASS: semantic search returns relevant results (1.8s)
- PASS: search handles no results (1.2s)
- FAIL: search with special characters (0.9s)
### Wallet - 接続
- user can connect MetaMask (3.1s)
- ⚠️ user can connect Phantom (2.8s) - 不安定
- user can disconnect wallet (1.5s)
- PASS: user can connect MetaMask (3.1s)
- WARNING: user can connect Phantom (2.8s) - 不安定
- PASS: user can disconnect wallet (1.5s)
### Trading - コアフロー
- user can place buy order (5.2s)
- user can place sell order (4.8s)
- insufficient balance shows error (1.9s)
- PASS: user can place buy order (5.2s)
- FAIL: user can place sell order (4.8s)
- PASS: insufficient balance shows error (1.9s)
## 失敗したテスト
@@ -623,13 +623,13 @@ jobs:
## 成功指標
E2Eテスト実行後:
- すべての重要なジャーニーが成功100%
- 全体の成功率 > 95%
- 不安定率 < 5%
- デプロイをブロックする失敗したテストなし
- アーティファクトがアップロードされアクセス可能
- テスト時間 < 10分
- HTMLレポートが生成された
- PASS: すべての重要なジャーニーが成功100%
- PASS: 全体の成功率 > 95%
- PASS: 不安定率 < 5%
- PASS: デプロイをブロックする失敗したテストなし
- PASS: アーティファクトがアップロードされアクセス可能
- PASS: テスト時間 < 10分
- PASS: HTMLレポートが生成された
---

View File

@@ -146,22 +146,22 @@ e) テストがまだ合格することを確認
### 1. 未使用のインポート
```typescript
// 未使用のインポートを削除
// FAIL: 未使用のインポートを削除
import { useState, useEffect, useMemo } from 'react' // useStateのみ使用
// 使用されているもののみを保持
// PASS: 使用されているもののみを保持
import { useState } from 'react'
```
### 2. デッドコードブランチ
```typescript
// 到達不可能なコードを削除
// FAIL: 到達不可能なコードを削除
if (false) {
// これは決して実行されない
doSomething()
}
// 未使用の関数を削除
// FAIL: 未使用の関数を削除
export function unusedHelper() {
// コードベースに参照なし
}
@@ -169,18 +169,18 @@ export function unusedHelper() {
### 3. 重複コンポーネント
```typescript
// 複数の類似コンポーネント
// FAIL: 複数の類似コンポーネント
components/Button.tsx
components/PrimaryButton.tsx
components/NewButton.tsx
// 1つに統合
// PASS: 1つに統合
components/Button.tsx (variantプロップ付き)
```
### 4. 未使用の依存関係
```json
// インストールされているがインポートされていないパッケージ
// FAIL: インストールされているがインポートされていないパッケージ
{
"dependencies": {
"lodash": "^4.17.21", // どこでも使用されていない
@@ -240,7 +240,7 @@ components/Button.tsx (variantプロップ付き)
- 依存関係: -Xパッケージ
### リスクレベル
🟢 低 - 検証可能な未使用コードのみを削除
低 - 検証可能な未使用コードのみを削除
詳細はDELETION_LOG.mdを参照してください。
```
@@ -294,12 +294,12 @@ components/Button.tsx (variantプロップ付き)
## 成功指標
クリーンアップセッション後:
- すべてのテストが合格
- ビルドが成功
- コンソールエラーなし
- DELETION_LOG.mdが更新された
- バンドルサイズが削減された
- 本番環境で回帰なし
- PASS: すべてのテストが合格
- PASS: ビルドが成功
- PASS: コンソールエラーなし
- PASS: DELETION_LOG.mdが更新された
- PASS: バンドルサイズが削減された
- PASS: 本番環境で回帰なし
---

View File

@@ -184,12 +184,12 @@ APIセキュリティ:
### 1. ハードコードされたシークレット(重要)
```javascript
// 重要: ハードコードされたシークレット
// FAIL: 重要: ハードコードされたシークレット
const apiKey = "sk-proj-xxxxx"
const password = "admin123"
const token = "ghp_xxxxxxxxxxxx"
// 正しい: 環境変数
// PASS: 正しい: 環境変数
const apiKey = process.env.OPENAI_API_KEY
if (!apiKey) {
throw new Error('OPENAI_API_KEY not configured')
@@ -199,11 +199,11 @@ if (!apiKey) {
### 2. SQLインジェクション重要
```javascript
// 重要: SQLインジェクションの脆弱性
// FAIL: 重要: SQLインジェクションの脆弱性
const query = `SELECT * FROM users WHERE id = ${userId}`
await db.query(query)
// 正しい: パラメータ化されたクエリ
// PASS: 正しい: パラメータ化されたクエリ
const { data } = await supabase
.from('users')
.select('*')
@@ -213,11 +213,11 @@ const { data } = await supabase
### 3. コマンドインジェクション(重要)
```javascript
// 重要: コマンドインジェクション
// FAIL: 重要: コマンドインジェクション
const { exec } = require('child_process')
exec(`ping ${userInput}`, callback)
// 正しい: シェルコマンドではなくライブラリを使用
// PASS: 正しい: シェルコマンドではなくライブラリを使用
const dns = require('dns')
dns.lookup(userInput, callback)
```
@@ -225,10 +225,10 @@ dns.lookup(userInput, callback)
### 4. クロスサイトスクリプティングXSS
```javascript
// 高: XSS脆弱性
// FAIL: 高: XSS脆弱性
element.innerHTML = userInput
// 正しい: textContentを使用またはサニタイズ
// PASS: 正しい: textContentを使用またはサニタイズ
element.textContent = userInput
// または
import DOMPurify from 'dompurify'
@@ -238,10 +238,10 @@ element.innerHTML = DOMPurify.sanitize(userInput)
### 5. サーバーサイドリクエストフォージェリSSRF
```javascript
// 高: SSRF脆弱性
// FAIL: 高: SSRF脆弱性
const response = await fetch(userProvidedUrl)
// 正しい: URLを検証してホワイトリスト
// PASS: 正しい: URLを検証してホワイトリスト
const allowedDomains = ['api.example.com', 'cdn.example.com']
const url = new URL(userProvidedUrl)
if (!allowedDomains.includes(url.hostname)) {
@@ -253,10 +253,10 @@ const response = await fetch(url.toString())
### 6. 安全でない認証(重要)
```javascript
// 重要: 平文パスワード比較
// FAIL: 重要: 平文パスワード比較
if (password === storedPassword) { /* ログイン */ }
// 正しい: ハッシュ化されたパスワード比較
// PASS: 正しい: ハッシュ化されたパスワード比較
import bcrypt from 'bcrypt'
const isValid = await bcrypt.compare(password, hashedPassword)
```
@@ -264,13 +264,13 @@ const isValid = await bcrypt.compare(password, hashedPassword)
### 7. 不十分な認可(重要)
```javascript
// 重要: 認可チェックなし
// FAIL: 重要: 認可チェックなし
app.get('/api/user/:id', async (req, res) => {
const user = await getUser(req.params.id)
res.json(user)
})
// 正しい: ユーザーがリソースにアクセスできることを確認
// PASS: 正しい: ユーザーがリソースにアクセスできることを確認
app.get('/api/user/:id', authenticateUser, async (req, res) => {
if (req.user.id !== req.params.id && !req.user.isAdmin) {
return res.status(403).json({ error: 'Forbidden' })
@@ -283,13 +283,13 @@ app.get('/api/user/:id', authenticateUser, async (req, res) => {
### 8. 金融操作の競合状態(重要)
```javascript
// 重要: 残高チェックの競合状態
// FAIL: 重要: 残高チェックの競合状態
const balance = await getBalance(userId)
if (balance >= amount) {
await withdraw(userId, amount) // 別のリクエストが並行して出金できる!
}
// 正しい: ロック付きアトミックトランザクション
// PASS: 正しい: ロック付きアトミックトランザクション
await db.transaction(async (trx) => {
const balance = await trx('balances')
.where({ user_id: userId })
@@ -309,13 +309,13 @@ await db.transaction(async (trx) => {
### 9. 不十分なレート制限(高)
```javascript
// 高: レート制限なし
// FAIL: 高: レート制限なし
app.post('/api/trade', async (req, res) => {
await executeTrade(req.body)
res.json({ success: true })
})
// 正しい: レート制限
// PASS: 正しい: レート制限
import rateLimit from 'express-rate-limit'
const tradeLimiter = rateLimit({
@@ -333,10 +333,10 @@ app.post('/api/trade', tradeLimiter, async (req, res) => {
### 10. 機密データのロギング(中)
```javascript
// 中: 機密データのロギング
// FAIL: 中: 機密データのロギング
console.log('User login:', { email, password, apiKey })
// 正しい: ログをサニタイズ
// PASS: 正しい: ログをサニタイズ
console.log('User login:', {
email: email.replace(/(?<=.).(?=.*@)/g, '*'),
passwordProvided: !!password
@@ -358,7 +358,7 @@ console.log('User login:', {
- **高い問題:** Y
- **中程度の問題:** Z
- **低い問題:** W
- **リスクレベル:** 🔴 高 / 🟡 中 / 🟢
- **リスクレベル:** 高 / 中 / 低
## 重要な問題(即座に修正)
@@ -380,7 +380,7 @@ console.log('User login:', {
**修復:**
```javascript
// 安全な実装
// PASS: 安全な実装
```
**参考資料:**
@@ -433,7 +433,7 @@ PRをレビューする際、インラインコメントを投稿:
## セキュリティレビュー
**レビューアー:** security-reviewer agent
**リスクレベル:** 🔴 高 / 🟡 中 / 🟢
**リスクレベル:** 高 / 中 / 低
### ブロッキング問題
- [ ] **重要**: [説明] @ `file:line`
@@ -532,13 +532,13 @@ npm install --save-dev audit-ci
## 成功指標
セキュリティレビュー後:
- 重要な問題が見つからない
- すべての高い問題が対処されている
- セキュリティチェックリストが完了
- コードにシークレットがない
- 依存関係が最新
- テストにセキュリティシナリオが含まれている
- ドキュメントが更新されている
- PASS: 重要な問題が見つからない
- PASS: すべての高い問題が対処されている
- PASS: セキュリティチェックリストが完了
- PASS: コードにシークレットがない
- PASS: 依存関係が最新
- PASS: テストにセキュリティシナリオが含まれている
- PASS: ドキュメントが更新されている
---

View File

@@ -220,26 +220,26 @@ jest.mock('@/lib/openai', () => ({
## テストの悪臭(アンチパターン)
### 実装の詳細をテスト
### FAIL: 実装の詳細をテスト
```typescript
// 内部状態をテストしない
expect(component.state.count).toBe(5)
```
### ユーザーに見える動作をテスト
### PASS: ユーザーに見える動作をテスト
```typescript
// ユーザーが見るものをテストする
expect(screen.getByText('Count: 5')).toBeInTheDocument()
```
### テストが互いに依存
### FAIL: テストが互いに依存
```typescript
// 前のテストに依存しない
test('creates user', () => { /* ... */ })
test('updates same user', () => { /* 前のテストが必要 */ })
```
### 独立したテスト
### PASS: 独立したテスト
```typescript
// 各テストでデータをセットアップ
test('updates user', () => {