Merge pull request #291 from nrslib/release/v0.18.1

Release v0.18.1
This commit is contained in:
nrs 2026-02-18 11:06:48 +09:00 committed by GitHub
commit d1b0ddee4e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 262 additions and 9 deletions

View File

@ -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

View File

@ -148,6 +148,61 @@ 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.
```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
| Category | Check Items |

View File

@ -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

View File

@ -148,6 +148,61 @@ 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)
}
```
### 認可とリゾルバーの整合性
テナントリゾルバーが特定ロール(例: スタッフ)を前提とする場合、エンドポイントに対応する認可制御が必要。認可なしだと、前提外のロールがアクセスしてリゾルバーが失敗する。
```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 チェックリスト
| カテゴリ | 確認事項 |

View File

@ -13,6 +13,7 @@
| ファイルサイズ | 目安として300行。タスクに応じて柔軟に |
| ボーイスカウト | 触った箇所は少し改善して去る |
| Fail Fast | エラーは早期に検出。握りつぶさない |
| プロジェクトスクリプト優先 | ツール実行はプロジェクト定義のスクリプトを使う。直接実行は最後の手段 |
## フォールバック・デフォルト引数の禁止
@ -288,3 +289,4 @@ function formatPercentage(value: number): string { ... }
- **内部実装のパブリック API エクスポート** - 公開するのはドメイン操作の関数・型のみ。インフラ層の関数や内部クラスをエクスポートしない
- **リファクタリング後の旧コード残存** - 置き換えたコード・エクスポートは削除する。明示的に残すよう指示されない限り残さない
- **安全機構を迂回するワークアラウンド** - 根本修正が正しいなら追加の迂回は不要
- **プロジェクトスクリプトを迂回するツール直接実行** - `npx tool` 等の直接実行は lockfile を迂回しバージョン不一致を起こす。プロジェクトが定義したスクリプトnpm scripts, Makefile 等)を探して使う。見つからない場合のみ直接実行を検討する

View File

@ -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

139
package-lock.json generated
View File

@ -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",

View File

@ -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",