From 425f929134d7e9f81cae0f44bb63a9541c4c12ce Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Wed, 18 Feb 2026 09:58:00 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=87=E3=82=A3?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=83=9D=E3=83=AA=E3=82=B7=E3=83=BC=E3=81=AB?= =?UTF-8?q?=E3=80=8C=E3=83=97=E3=83=AD=E3=82=B8=E3=82=A7=E3=82=AF=E3=83=88?= =?UTF-8?q?=E3=82=B9=E3=82=AF=E3=83=AA=E3=83=97=E3=83=88=E5=84=AA=E5=85=88?= =?UTF-8?q?=E3=80=8D=E3=83=AB=E3=83=BC=E3=83=AB=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit npx等の直接実行によるlockfile迂回を防ぐため、プロジェクト定義のスクリプトを優先する原則とREJECT項目を追加 --- builtins/en/policies/coding.md | 2 ++ builtins/ja/policies/coding.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/builtins/en/policies/coding.md b/builtins/en/policies/coding.md index 1296a9a..d658b28 100644 --- a/builtins/en/policies/coding.md +++ b/builtins/en/policies/coding.md @@ -13,6 +13,7 @@ Prioritize correctness over speed, and code accuracy over ease of implementation | File size | ~300 lines as a guideline. Be flexible depending on the task | | Boy Scout | Leave touched areas a little better than you found them | | Fail Fast | Detect errors early. Never swallow them | +| Project scripts first | Use project-defined scripts for tool execution. Direct invocation is a last resort | ## No Fallbacks or Default Arguments @@ -288,3 +289,4 @@ function formatPercentage(value: number): string { ... } - **Internal implementation exported from public API** - Only export domain-level functions and types. Do not export infrastructure functions or internal classes - **Replaced code surviving after refactoring** - Remove replaced code and exports. Do not keep unless explicitly told to - **Workarounds that bypass safety mechanisms** - If the root fix is correct, no additional bypass is needed +- **Direct tool execution bypassing project scripts** - `npx tool` and similar bypass the lockfile, causing version mismatches. Look for project-defined scripts (npm scripts, Makefile, etc.) first. Only consider direct execution when no script exists diff --git a/builtins/ja/policies/coding.md b/builtins/ja/policies/coding.md index dafb098..df77a33 100644 --- a/builtins/ja/policies/coding.md +++ b/builtins/ja/policies/coding.md @@ -13,6 +13,7 @@ | ファイルサイズ | 目安として300行。タスクに応じて柔軟に | | ボーイスカウト | 触った箇所は少し改善して去る | | Fail Fast | エラーは早期に検出。握りつぶさない | +| プロジェクトスクリプト優先 | ツール実行はプロジェクト定義のスクリプトを使う。直接実行は最後の手段 | ## フォールバック・デフォルト引数の禁止 @@ -288,3 +289,4 @@ function formatPercentage(value: number): string { ... } - **内部実装のパブリック API エクスポート** - 公開するのはドメイン操作の関数・型のみ。インフラ層の関数や内部クラスをエクスポートしない - **リファクタリング後の旧コード残存** - 置き換えたコード・エクスポートは削除する。明示的に残すよう指示されない限り残さない - **安全機構を迂回するワークアラウンド** - 根本修正が正しいなら追加の迂回は不要 +- **プロジェクトスクリプトを迂回するツール直接実行** - `npx tool` 等の直接実行は lockfile を迂回しバージョン不一致を起こす。プロジェクトが定義したスクリプト(npm scripts, Makefile 等)を探して使う。見つからない場合のみ直接実行を検討する From 6153fd880abeea8d09de4f4ec1e9492a4efe44a2 Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Wed, 18 Feb 2026 10:25:27 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=E3=82=BB=E3=82=AD=E3=83=A5=E3=83=AA?= =?UTF-8?q?=E3=83=86=E3=82=A3=E3=83=8A=E3=83=AC=E3=83=83=E3=82=B8=E3=81=AB?= =?UTF-8?q?=E3=83=9E=E3=83=AB=E3=83=81=E3=83=86=E3=83=8A=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=83=87=E3=83=BC=E3=82=BF=E5=88=86=E9=9B=A2=E3=82=BB=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builtins/en/knowledge/security.md | 40 +++++++++++++++++++++++++++++++ builtins/ja/knowledge/security.md | 40 +++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/builtins/en/knowledge/security.md b/builtins/en/knowledge/security.md index 6c9d844..de2b945 100644 --- a/builtins/en/knowledge/security.md +++ b/builtins/en/knowledge/security.md @@ -148,6 +148,46 @@ if (!safePath.startsWith(path.resolve(baseDir))) { - Resource exhaustion attack possibility → Warning - Infinite loop possibility → REJECT +## Multi-Tenant Data Isolation + +Prevent data access across tenant boundaries. Authorization (who can operate) and scoping (which tenant's data) are separate concerns. + +| Criteria | Verdict | +|----------|---------| +| Reads are tenant-scoped but writes are not | REJECT | +| Write operations use client-provided tenant ID | REJECT | +| Endpoint using tenant resolver has no authorization control | REJECT | +| Some paths in role-based branching don't account for tenant resolution | REJECT | + +### Read-Write Consistency + +Apply tenant scoping to both reads and writes. Scoping only one side creates a state where data cannot be viewed but can be modified. + +When adding a tenant filter to reads, always add tenant verification to corresponding writes. + +### Write-Side Tenant Verification + +For write operations, use the tenant ID resolved from the authenticated user, not from the request body. + +```kotlin +// NG - Trusting client-provided tenant ID +fun create(request: CreateRequest) { + service.create(request.tenantId, request.data) +} + +// OK - Resolve tenant from authentication +fun create(request: CreateRequest) { + val tenantId = tenantResolver.resolve() + service.create(tenantId, request.data) +} +``` + +### Authorization-Resolver Alignment + +When a tenant resolver assumes a specific role (e.g., staff), the endpoint must have corresponding authorization controls. Without authorization, unexpected roles can access the endpoint and cause the resolver to fail. + +For endpoints with role-based branching, verify that tenant resolution succeeds on all paths. + ## OWASP Top 10 Checklist | Category | Check Items | diff --git a/builtins/ja/knowledge/security.md b/builtins/ja/knowledge/security.md index 14ab513..1e5d91a 100644 --- a/builtins/ja/knowledge/security.md +++ b/builtins/ja/knowledge/security.md @@ -148,6 +148,46 @@ if (!safePath.startsWith(path.resolve(baseDir))) { - リソース枯渇攻撃の可能性 → 警告 - 無限ループの可能性 → REJECT +## マルチテナントデータ分離 + +テナント境界を超えたデータアクセスを防ぐ。認可(誰が操作できるか)とスコーピング(どのテナントのデータか)は別の関心事。 + +| 基準 | 判定 | +|------|------| +| 読み取りはテナントスコープだが書き込みはスコープなし | REJECT | +| 書き込み操作でクライアント提供のテナントIDを使用 | REJECT | +| テナントリゾルバーを使うエンドポイントに認可制御がない | REJECT | +| ロール分岐の一部パスでテナント解決が未考慮 | REJECT | + +### 読み書きの一貫性 + +テナントスコーピングは読み取りと書き込みの両方に適用する。片方だけでは、参照できないが変更できる状態が生まれる。 + +読み取りにテナントフィルタを追加したら、対応する書き込みも必ずテナント検証する。 + +### 書き込みのテナント検証 + +書き込み操作では、リクエストボディのテナントIDではなく認証済みユーザーから解決したテナントIDを使う。 + +```kotlin +// NG - クライアント提供のテナントIDを信頼 +fun create(request: CreateRequest) { + service.create(request.tenantId, request.data) +} + +// OK - 認証情報からテナントを解決 +fun create(request: CreateRequest) { + val tenantId = tenantResolver.resolve() + service.create(tenantId, request.data) +} +``` + +### 認可とリゾルバーの整合性 + +テナントリゾルバーが特定ロール(例: スタッフ)を前提とする場合、エンドポイントに対応する認可制御が必要。認可なしだと、前提外のロールがアクセスしてリゾルバーが失敗する。 + +ロール分岐があるエンドポイントでは、全パスでテナント解決が成功するか検証する。 + ## OWASP Top 10 チェックリスト | カテゴリ | 確認事項 | From fc3b62ee1c8a4ac8dd8d044e78fc8d96833decf6 Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Wed, 18 Feb 2026 10:29:39 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=E8=AA=8D=E5=8F=AF=E3=81=A8=E3=83=AA?= =?UTF-8?q?=E3=82=BE=E3=83=AB=E3=83=90=E3=83=BC=E3=81=AE=E6=95=B4=E5=90=88?= =?UTF-8?q?=E6=80=A7=E3=82=BB=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AB?= =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89=E4=BE=8B=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- builtins/en/knowledge/security.md | 15 +++++++++++++++ builtins/ja/knowledge/security.md | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/builtins/en/knowledge/security.md b/builtins/en/knowledge/security.md index de2b945..b9b9bc8 100644 --- a/builtins/en/knowledge/security.md +++ b/builtins/en/knowledge/security.md @@ -186,6 +186,21 @@ fun create(request: CreateRequest) { When a tenant resolver assumes a specific role (e.g., staff), the endpoint must have corresponding authorization controls. Without authorization, unexpected roles can access the endpoint and cause the resolver to fail. +```kotlin +// NG - Resolver assumes STAFF but no authorization control +fun getSettings(): SettingsResponse { + val tenantId = tenantResolver.resolve() // Fails for non-STAFF + return settingsService.getByTenant(tenantId) +} + +// OK - Authorization ensures correct role +@Authorized(roles = ["STAFF"]) +fun getSettings(): SettingsResponse { + val tenantId = tenantResolver.resolve() + return settingsService.getByTenant(tenantId) +} +``` + For endpoints with role-based branching, verify that tenant resolution succeeds on all paths. ## OWASP Top 10 Checklist diff --git a/builtins/ja/knowledge/security.md b/builtins/ja/knowledge/security.md index 1e5d91a..d51beea 100644 --- a/builtins/ja/knowledge/security.md +++ b/builtins/ja/knowledge/security.md @@ -186,6 +186,21 @@ fun create(request: CreateRequest) { テナントリゾルバーが特定ロール(例: スタッフ)を前提とする場合、エンドポイントに対応する認可制御が必要。認可なしだと、前提外のロールがアクセスしてリゾルバーが失敗する。 +```kotlin +// NG - リゾルバーが STAFF を前提とするが認可制御なし +fun getSettings(): SettingsResponse { + val tenantId = tenantResolver.resolve() // STAFF 以外で失敗 + return settingsService.getByTenant(tenantId) +} + +// OK - 認可制御でロールを保証 +@Authorized(roles = ["STAFF"]) +fun getSettings(): SettingsResponse { + val tenantId = tenantResolver.resolve() + return settingsService.getByTenant(tenantId) +} +``` + ロール分岐があるエンドポイントでは、全パスでテナント解決が成功するか検証する。 ## OWASP Top 10 チェックリスト From 78e8950656a6129baa81e2a6ef4f3848a58130b4 Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Wed, 18 Feb 2026 11:05:24 +0900 Subject: [PATCH 4/4] Release v0.18.1 --- CHANGELOG.md | 7 +++ docs/CHANGELOG.ja.md | 7 +++ package-lock.json | 139 ++++++++++++++++++++++++++++++++++++++++--- package.json | 4 +- 4 files changed, 148 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e60cf0b..4779cc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [0.18.1] - 2026-02-18 + +### Added + +- Added multi-tenant data isolation section and authorization-resolver consistency code examples to security knowledge +- Added "prefer project scripts" rule to coding policy — detects direct tool invocation (e.g., `npx vitest`) when equivalent npm scripts exist + ## [0.18.0] - 2026-02-17 ### Added diff --git a/docs/CHANGELOG.ja.md b/docs/CHANGELOG.ja.md index 4e63aa4..dda378d 100644 --- a/docs/CHANGELOG.ja.md +++ b/docs/CHANGELOG.ja.md @@ -6,6 +6,13 @@ フォーマットは [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) に基づいています。 +## [0.18.1] - 2026-02-18 + +### Added + +- セキュリティナレッジにマルチテナントデータ分離セクションと認可・リゾルバー整合性のコード例を追加 +- コーディングポリシーに「プロジェクトスクリプト優先」ルールを追加 — npm スクリプトが存在するのに直接ツール呼び出し(例: `npx vitest`)を検出 + ## [0.18.0] - 2026-02-17 ### Added diff --git a/package-lock.json b/package-lock.json index 855e0a1..839a403 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "takt", - "version": "0.18.0", + "version": "0.18.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "takt", - "version": "0.18.0", + "version": "0.18.1", "license": "MIT", "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.37", - "@openai/codex-sdk": "^0.98.0", + "@openai/codex-sdk": "^0.103.0", "@opencode-ai/sdk": "^1.1.53", "chalk": "^5.3.0", "commander": "^12.1.0", @@ -928,15 +928,140 @@ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true }, - "node_modules/@openai/codex-sdk": { - "version": "0.98.0", - "resolved": "https://registry.npmjs.org/@openai/codex-sdk/-/codex-sdk-0.98.0.tgz", - "integrity": "sha512-TbPgrBpuSNMJyOXys0HNsh6UoP5VIHu1fVh2KDdACi5XyB0vuPtzBZC+qOsxHz7WXEQPFlomPLyxS6JnE5Okmg==", + "node_modules/@openai/codex": { + "version": "0.103.0", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.103.0.tgz", + "integrity": "sha512-kf7sytd/2mMUKYK8eKgNrwujgNjWrfWFMFfRCOVIFBeByQXmtxe2giVEo44paKDapaS7dQnxqwcUY2OOYrdfWA==", "license": "Apache-2.0", + "bin": { + "codex": "bin/codex.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "@openai/codex-darwin-arm64": "npm:@openai/codex@0.103.0-darwin-arm64", + "@openai/codex-darwin-x64": "npm:@openai/codex@0.103.0-darwin-x64", + "@openai/codex-linux-arm64": "npm:@openai/codex@0.103.0-linux-arm64", + "@openai/codex-linux-x64": "npm:@openai/codex@0.103.0-linux-x64", + "@openai/codex-win32-arm64": "npm:@openai/codex@0.103.0-win32-arm64", + "@openai/codex-win32-x64": "npm:@openai/codex@0.103.0-win32-x64" + } + }, + "node_modules/@openai/codex-darwin-arm64": { + "name": "@openai/codex", + "version": "0.103.0-darwin-arm64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.103.0-darwin-arm64.tgz", + "integrity": "sha512-TGMMiB/A8CxG7ghhimEBUCk8nTEXef9cCSh+wUuq3dizAS/PuuHHf6m8LUeZCtkl/XMUxxDqEv3+i/jcB0Jo4A==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@openai/codex-darwin-x64": { + "name": "@openai/codex", + "version": "0.103.0-darwin-x64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.103.0-darwin-x64.tgz", + "integrity": "sha512-1aJnXu6dYsu25fBX+76I150gh1BaKoni9wq6QWEPQ3x+FDqsCgGWG5G6G0v4uVMvTgXB0A6IhwPj88ZYi0BsWw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@openai/codex-linux-arm64": { + "name": "@openai/codex", + "version": "0.103.0-linux-arm64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.103.0-linux-arm64.tgz", + "integrity": "sha512-LdrBhfhV2ZiJBof5h1zxKgmEdt2vm5m8/xEtZShiqSDkgfsTUHNoOAb0WmgITF8+4kyWZtpMjBLw3XjkmNYQJw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@openai/codex-linux-x64": { + "name": "@openai/codex", + "version": "0.103.0-linux-x64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.103.0-linux-x64.tgz", + "integrity": "sha512-wRjcldkssdCUFQ/YoHRAbgQhszhGyt14Qa/V5ouQp4i3j5dPItVwl/y64u2Fe6fc3OAJkw6W3MJCSO1F323y3g==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@openai/codex-sdk": { + "version": "0.103.0", + "resolved": "https://registry.npmjs.org/@openai/codex-sdk/-/codex-sdk-0.103.0.tgz", + "integrity": "sha512-wMuYUIs5ajZsE2mnacYQKubt61cykMCAyNhPHBINpf9AJkiuvpAHtuz7npXzk9z4wmfqXPMXIQ3x/v1QMmd0cQ==", + "license": "Apache-2.0", + "dependencies": { + "@openai/codex": "0.103.0" + }, "engines": { "node": ">=18" } }, + "node_modules/@openai/codex-win32-arm64": { + "name": "@openai/codex", + "version": "0.103.0-win32-arm64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.103.0-win32-arm64.tgz", + "integrity": "sha512-d5yXLt9m4/3Wf3hiUcygr7voPXgLrvTgbtoYiAqZl/rtoNVRhChjt09EJKRlFQusq3aUGyQduqTLND2uF5dwBA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@openai/codex-win32-x64": { + "name": "@openai/codex", + "version": "0.103.0-win32-x64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.103.0-win32-x64.tgz", + "integrity": "sha512-WtR5y3KeaGjLeYWQnRhMYBptm+eRMvwVlHJpIlFSw7DzD1idvL6ALZ4+kBL1rXBZMJzzpXomax7vMNTlXFjCOg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16" + } + }, "node_modules/@opencode-ai/sdk": { "version": "1.1.53", "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.1.53.tgz", diff --git a/package.json b/package.json index dcb67cf..4692a91 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "takt", - "version": "0.18.0", + "version": "0.18.1", "description": "TAKT: TAKT Agent Koordination Topology - AI Agent Piece Orchestration", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -60,7 +60,7 @@ ], "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.37", - "@openai/codex-sdk": "^0.98.0", + "@openai/codex-sdk": "^0.103.0", "@opencode-ai/sdk": "^1.1.53", "chalk": "^5.3.0", "commander": "^12.1.0",