Skip to content

Hotfix/issue62807 - #1914

Merged
mhaya merged 4 commits into
develop_v2.1.0from
hotfix/issue62807
Sep 16, 2026
Merged

mhaya merged 4 commits into
develop_v2.1.0from
hotfix/issue62807

Conversation

@mhaya

@mhaya mhaya commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

概要 (Summary)

  • 画面からアイテムを削除できない回帰を直す (issue62807)。 v2.0.4 (2f6b61b2f) で soft_delete ビューの所有者検証を record_edit_permission_required に切り出したが、このデコレータは recid を kwargs / request.form / JSON body / query string からしか探していなかった。画面の削除ボタンは POST /items/prepare_delete_item{"pid_value": ...} を投げ、weko_items_ui.views.prepare_delete_itemsoft_delete(del_value)位置引数でビュー関数を直接呼ぶ(weko_workflow.utils.prepare_delete_workflow も同じ)。kwargs は空、ボディのキーも recid ではないので id が取れず abort(400)。判定が権限チェックより前にあるため、作成者でも管理者でも一律に削除できないviews.py:1362 の呼び出しは try の外なので JSON ではなく素の 400 が返る)。
  • 修正は inspect.signature().bind_partial() でシグネチャに束ね、位置引数からも recid を解決する。in-process 呼び出しがあるのは soft_delete だけで、restore / copy_bucket / get_file_place / replace_file には無いことを確認済み。
  • 同じ死角を次から CI で止める。 台帳 (weko3_api_list_full.tsv) は extract_routes.py が拾う Flask のルートを単位にしているため、HTTP を通さずビュー関数を直接呼ぶ「第二の入口」がどの列にも現れない。issue62807 の行は auth_required=要 / test_gap=- で穴が無いように見えていた。audit_inprocess_views.py(AST で列挙・書き込みなし)と add_inproc_callers.py(台帳に inproc_callers 列を付与、62→63列)を追加し、--check --summary-only --gate を drift ワークフローのコンテナ起動前に挿入した(ソースと台帳だけで済むため)。

関連Issue / チケット (Related Issues)

  • close #62807

変更タイプ (Type of Change)

  • 🚀 新機能追加 (Feature)
  • 🐛 バグ修正 (Bug Fix)
  • 🔒 セキュリティ修正 (Security Fix)
  • 🚫 機能のクローズ・非公開化・削除 (Feature Deprecation/Disable)
  • ⚠️ 破壊的変更・データ移行を伴う修正 (Breaking Change / Migration)
  • 📚 仕様書・マニュアル・APIリストの更新 (Documentation)

🤖 0. CI 自動チェック (API Inventory Drift)

  • CI が PASS している、または FAIL の各項目に対処済み
    PR 作成後に確認する。台帳ブランチの警告(冒頭)を件数より先に見ること。
    下記のとおりプライベート側に同名ブランチがまだ無いため、現状は既定ブランチと
    比較され警告が出る見込み。

API を追加・変更した場合(必須)

ルーティング(url_map)は変えていない。 追加・削除・メソッド変更のあるエンドポイントは無く、
api_snapshot.json / weko3_api_list_full.tsv の行そのものは変わらない。
ただし 本 PR で追加した CI ステップが台帳の inproc_callers 列を読むため、
プライベートリポジトリ側の台帳に列を足す PR が必要。

  • プライベートリポジトリ側の作業ブランチを、この PR のブランチと同名 (hotfix/issue62807) で切った
    ⚠️ 未実施。 現在プライベート側は data/v2.0.4-git-columns で作業しており、
    同名ブランチが無い。先に切らないと、この PR の CI は既定ブランチの台帳
    inproc_callers 列なし)と比較され、新しい gate が正しく評価されない。
  • プライベートリポジトリapi_snapshot.json を更新し、対応する PR を出した
    ルート変更が無いためスナップショットの更新自体は不要。上記の同名ブランチ作成と
    inproc_callers 列追加の PR を、この PR とセットで出す。
  • プライベートリポジトリweko3_api_list_full.tsv に行を追加・更新し、
    build_checklist.py で 24 列版を再生成した
    行の追加は不要。add_inproc_callers.py で **inproc_callers 列(63列目)**を
    付与したうえで再生成する。

    bash export WEKO_API_INVENTORY_DIR=/path/to/weko-secret python3 tools/api-inventory/scripts/add_inproc_callers.py python3 tools/api-inventory/scripts/build_checklist.py
  • 台帳・スナップショット・実測結果をこの公開リポジトリにコミットしていない
    git status*.tsv / api_snapshot.json は出ていない。本 PR の差分はスクリプトと
    ワークフローと実装・テストのみ)

🔒 1. セキュリティ & API アクセス制御チェック (必須)

認証・認可 (Authentication & Authorization)

  • 新規/変更された Blueprint・View・REST リソースに適切なデコレータ / Permission を設定している
    デコレータの追加ではなく、既存 record_edit_permission_required
    恒久的に 400 で落ちていて権限判定に到達していなかったのを直すもの。
    判定本体は従来どおり check_created_id(作成者 / 共有ユーザ /
    コミュニティ管理者 / スーパーユーザ)。
  • 状態変更・破壊的メソッド (POST / PUT / PATCH / DELETE) の権限が正しく制限されている
    POST /records/soft_delete/<recid> と、そこへの in-process 呼び出しの両方で
    権限判定が実際に効くようになった(修正前は誰でも 400 = 誰も実行できない状態)。
  • 未ログイン(Anonymous)状態でアクセスした際、意図しないデータ取得・変更が拒絶される
    未認証 401 / 権限なし 403 / id が取れない 400 をテストで固定した。
  • 認可を config の permission factory に委ねている場合、None で無効化していない
    該当なし(*_PERMISSION_FACTORY は触っていない)。

レビュー時の注意: デコレータは呼び出し元のリクエストコンテキストで動く
request.form / JSON body を読むデコレータを in-process 呼び出しのあるビューに付けると
今回と同じ壊れ方をする。付けるなら位置引数からも値を解決できることを確かめること
tools/api-inventory/scripts/README.md に追記済み)。

機能クローズ・非公開化の場合 (Feature Disable)

  • 該当なし(機能の無効化・非公開化は含まない)。

🧪 2. テストコード観点チェック (pytest / Invenio Test Suite)

権限・異常系テスト (Negative & Authorization Tests)

  • 未認証アクセス (Anonymous): test_record_edit_permission_required_aborts で 401 を検証
  • 権限不足ユーザー (Forbidden): 同テストで check_created_id_by_recid が False のとき 403 を検証
    (修正前はここまで到達せず 400 に化けていた)
  • 無効化/非公開機能の遮断テスト: 該当なし

追加したテスト:

テスト 何を守るか
tests/test_views.py::test_soft_delete_called_in_process 本番の呼び出し形(soft_delete("1") / soft_delete("del_ver_1") を位置引数、ボディは {"pid_value": ...})をそのまま再現する結合テスト。修正前は 2 ケースとも 400 で落ちる
tests/test_permissions.py::test_record_edit_permission_required_id_sources recid の解決元 6 パターン(URL ルート kwargs / 位置引数 / 位置引数+del_ver_ prefix / form / JSON / query string)。check_created_id_by_recid には prefix を剥がした id、ビュー本体には受け取ったままの値を渡すことも検証
tests/test_permissions.py::test_record_edit_permission_required_aborts 401 / 400 / 403 の出し分け

境界値・入力バリデーションテスト (Boundary & Validation)

  • id がどこにも無いときに 400 を返すことを検証(test_record_edit_permission_required_aborts)。
    巨大ファイル・MIME・スキーマ検証は本 PR の変更範囲外。

データ整合性・トランザクションテスト (Integrity & Rollback)

  • 該当なし(認可判定の解決経路のみの変更で、DB/ストレージ操作には手を入れていない)

🛡️ 3. データ保護 & 破壊的変更防止チェック (Data Safety)

  • 物理削除・上書きの安全性:
    • 対象は論理削除 (soft_delete / delete_version)。削除処理そのものは変更していない
      変わったのは「どの id に対する削除かを認可デコレータが解決できるか」だけで、
      ビュー本体に渡る値(del_ver_ 付きのまま)は従来と同一。
    • 誤って別レコードに適用されないよう、del_ver_ prefix を剥がした id が
      権限判定に渡ることをテストで固定した。
  • トランザクション整合性: 変更なし(db.session の扱いに手を入れていない)

⚙️ 4. マイグレーション & システム影響チェック (Invenio / WEKO3 Stack)

該当なし。 DB スキーマ / Alembic、ES・OpenSearch のマッピング、invenio.cfg・環境変数、
Celery タスクのシグネチャ、キャッシュのいずれも変更していない。

データベース (DB / Alembic)

  • 該当なし

検索インデックス (Elasticsearch / OpenSearch)

  • 該当なし

設定 & 非同期処理 (Config / Celery / Cache)

  • 該当なし

📚 5. ドキュメント・仕様書更新チェック (weko-document)

  • API インベントリ: ツール側(本リポジトリ tools/api-inventory/scripts/README.md)に
    「★認可を足す前に in-process の呼び出し元を見る (issue62807)」節と、
    audit_inprocess_views.py / add_inproc_callers.py の入出力表を追記した。
    台帳・調査記録はプライベートリポジトリ側(§0 のとおり inproc_callers 列の追加が残っている)。
  • WEKO3 機能仕様書: 更新不要
  • 各種マニュアル (管理者 / 利用者マニュアル): 更新不要
  • 更新不要な場合(理由): 画面導線・操作手順・権限仕様は変えていない。
    v2.0.4 で意図せず壊れた「作成者・管理者はアイテムを削除できる」という
    既存仕様どおりの挙動に戻すだけ
    で、仕様書・マニュアルの記述とのズレは生じない。

📋 6. 動作検証エビデンス (Verification Evidence)

テスト実行結果

docker compose run --rm --no-deps -T web bash /code/scripts/ci/run-module-tests.sh weko-records-ui
# または コンテナ内で:
cd /code/modules/weko-records-ui && \
  pytest tests/test_views.py::test_soft_delete_called_in_process \
         tests/test_permissions.py::test_record_edit_permission_required_id_sources \
         tests/test_permissions.py::test_record_edit_permission_required_aborts
# -> 実行結果を貼る(未実行)

⚠️ モジュールテストはこの PR 作成時点で未実行。 マージ前に上記を流して結果を貼ること。
修正前のコードでは、位置引数の 2 ケースと 403 ケースが 400 に化けて落ちることが
このテストの効いている証拠になるので、可能なら revert した状態でも一度流す。

CI の成果物 (artifact: api-inventory-summary)

ファイル 内容
drift.md ベースラインとの差分(件数のみ
reconcile.md 台帳と実機の突き合わせ(件数のみ

本 PR で追加した in-process チェックはコンテナ起動前に走るので、
落ちた場合は上記 artifact ではなく Actions のログ(Check in-process view callers)を見る。
明細(該当したビュー名・呼び出し元)は公開できないため出力しない。
プライベートリポジトリ側で --summary-only なしで再実行して確認する。

手動で確認したこと

  • in-process 呼び出しの棚卸し(ソース静的解析、公開リポジトリのソースのみ):
    WEKO_ROOT=/home/mhaya/wekov2 python3 tools/api-inventory/scripts/audit_inprocess_views.py --summary-only
    # -> in-process から呼ばれるビュー: 5 件 (HIGH=5 MEDIUM=0 LOW=0)
    内訳は issue62807 の soft_delete 2 件と、HeadlessActivity
    prepare_edit_item / prepare_delete_item / check_validation_error_msg
    位置引数で呼ぶ 3 件(v2.0.4 以前から存在)。
    後者のデコレータはリクエストから値を読まないため同じ失敗はしないが、
    prepare_delete_item 経由で issue62807 の影響は受ける。
  • 呼び出し元の確認: soft_delete ビューを in-process で呼ぶのは
    weko_items_ui/views.py:1381weko_workflow/utils.py:1981 の 2 箇所。
    weko_workflow/views.py:1915soft_delete
    weko_records_ui.utils.soft_delete(実装側)で、ビュー関数ではないため影響なし。
  • 他ビューへの波及なし: 同じデコレータを使う restore / copy_bucket /
    get_file_place / replace_file に in-process 呼び出しが無いことを確認。
  • ⚠️ 未実施: 実機(画面)での削除操作の確認と、ワークフロー経由の削除
    (prepare_delete_workflow) の確認。

Summary by Sourcery

Fix item deletion by supporting positional record IDs in authorization checks and add CI safeguards for in-process view entry points.

Bug Fixes:

  • Restore item deletion through in-process calls by resolving record IDs supplied as positional view arguments before authorization checks.

Enhancements:

  • Add regression coverage for record ID resolution across route, positional argument, form, JSON, and query-string inputs, including authentication and authorization failures.
  • Add static analysis and API-inventory support for tracking in-process view callers so authorization changes do not overlook non-HTTP entry points.

CI:

  • Run the in-process view caller inventory gate before starting CI containers.

Documentation:

  • Document in-process view caller auditing and the new inventory tooling and column.

Tests:

  • Add regression tests covering soft deletion via positional arguments and the expected 401, 400, and 403 authorization outcomes.

mhaya and others added 4 commits September 7, 2026 01:00
v2.0.4 (2f6b61b) で soft_delete ビューの所有者検証を
record_edit_permission_required に切り出したが、このデコレータは recid を
kwargs / request.form / JSON body / query string からしか探していなかった。

画面の削除ボタンは POST /items/prepare_delete_item に {"pid_value": ...} を
投げ、weko_items_ui.views.prepare_delete_item がビュー関数を
soft_delete(del_value) と *位置引数* で直接呼ぶ
(weko_workflow.utils.prepare_delete_workflow も同じ)。
このとき kwargs は空、ボディのキーも recid ではなく pid_value なので、
recid が見つからず abort(400) していた。判定は権限チェックより前なので、
作成者でも管理者でも一律にアイテムを削除できない。
views.py:1362 の呼び出しは try の外にあり、JSON ではなく素の 400 が返る。

inspect.signature().bind_partial() でシグネチャに束ね、位置引数からも
recid を解決するようにした。in-process 呼び出しがあるのは soft_delete だけで、
restore / copy_bucket / get_file_place / replace_file には無い。

テストは本番の呼び出し形をそのまま再現する結合テスト (test_views.py) と、
recid の解決元 6 パターン + 401/400/403 を見る単体テスト
(test_permissions.py) を追加した。修正前は位置引数の 2 ケースと
403 ケースが 400 に化けて落ちる。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NJUSCriemeQBj1WU7ttXV1
台帳は extract_routes.py が拾う Flask のルートを単位にしている。だが WEKO3 には
ビュー関数を HTTP を通さず別モジュールから直接呼ぶ「第二の入口」があり、
これは台帳のどの列にも現れない。ルート単位で認可を足していく作業では素通りする。

issue62807 がその実例。soft_delete の行は auth_required=要 / test_gap=- で
穴が無いように見えていたが、実際には prepare_delete_item が
soft_delete(del_value) と位置引数で直接呼んでおり、v2.0.4 で足した
record_edit_permission_required が recid を見つけられず abort(400) していた。

- audit_inprocess_views.py: ルート登録されたビューのうち、本体コードから
  名前で import されているものを AST で列挙する。認可デコレータ付きを
  位置引数で呼んでいるものは risk=HIGH。
- add_inproc_callers.py: 台帳に inproc_callers 列を付与する (62→63列)。
  --check は書き込まず、台帳がまだ知らない呼び出し元だけを報告する。
- api-inventory-drift.yml: --check --summary-only --gate を CI に入れた。
  ソースと台帳だけで済むのでコンテナ起動の前に流す。
- README: ケース1 の手順、認可を足す前の確認、スクリプトの入出力表を更新。

現状の検出結果は HIGH 5 件。うち 2 件が issue62807 (soft_delete)、
残り 3 件は HeadlessActivity が prepare_edit_item / prepare_delete_item /
check_validation_error_msg を位置引数で呼ぶもの (v2.0.4 以前から存在)。
後者のデコレータはリクエストから値を読まないため同じ失敗はしないが、
prepare_delete_item 経由で issue62807 の影響は受ける。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NJUSCriemeQBj1WU7ttXV1
@sourcery-ai

sourcery-ai Bot commented Sep 16, 2026

Copy link
Copy Markdown

Reviewer's Guide

このPRは、HTTP以外から位置引数で直接呼ばれる soft_delete でも認可デコレータがレコードIDを正しく解決できるようにして、画面削除の回帰を修正します。結合・権限テストを追加するとともに、ASTベースのin-process呼び出し元棚卸しをAPI台帳とCI gateに組み込み、同種の認可漏れ・回帰を検出できるようにしています。

Sequence diagram for in-process item deletion authorization

sequenceDiagram
    participant User
    participant PrepareDelete as prepare_delete_item
    participant SoftDelete as soft_delete
    participant Permission as record_edit_permission_required
    participant Checker as check_created_id_by_recid

    User->>PrepareDelete: POST /items/prepare_delete_item
    PrepareDelete->>SoftDelete: soft_delete(del_value)
    SoftDelete->>Permission: Resolve recid from bound positional argument
    Permission->>Checker: check_created_id_by_recid(recid)
    alt authorized
        Checker-->>Permission: allowed
        Permission-->>SoftDelete: invoke view with original del_value
        SoftDelete-->>PrepareDelete: logical deletion result
    else unauthorized
        Checker-->>Permission: denied
        Permission-->>SoftDelete: 403
    end
Loading

Flow diagram for record ID resolution in the permission decorator

flowchart TD
    A[record_edit_permission_required] --> B{Authenticated?}
    B -- No --> C[401]
    B -- Yes --> D{recid in kwargs?}
    D -- Yes --> H[Normalize recid]
    D -- No --> E{recid in bound positional arguments?}
    E -- Yes --> H
    E -- No --> F{recid in form, JSON, or query string?}
    F -- No --> G[400]
    F -- Yes --> H
    H --> I[check_created_id_by_recid]
    I -- Allowed --> J[Call protected view]
    I -- Denied --> K[403]
Loading

File-Level Changes

Change Details Files
修正認可デコレータによる位置引数のレコードID解決
  • 関数シグネチャへのバインドで位置引数から recid を解決
  • URL引数、form、JSON、query string の既存解決経路を維持
  • 認可判定には del_ver_ プレフィックス除去後のIDを渡し、ビュー本体には元の値を渡す
modules/weko-records-ui/weko_records_ui/permissions.py
画面およびワークフロー経由の論理削除回帰をテストで固定
  • soft_delete の位置引数呼び出しで通常削除とバージョン削除を検証
  • recid の6つの入力元と 401/400/403 の異常系を検証
modules/weko-records-ui/tests/test_permissions.py
modules/weko-records-ui/tests/test_views.py
in-process ビュー呼び出しをAPI台帳とCIで検出
  • AST解析でルート化されたビューの別モジュールからの直接呼び出しを列挙
  • 台帳に inproc_callers 列を付与するスクリプトを追加
  • 未記録の呼び出し元を検出してCI gateでコンテナ起動前に失敗させる
tools/api-inventory/scripts/audit_inprocess_views.py
tools/api-inventory/scripts/add_inproc_callers.py
.github/workflows/api-inventory-drift.yml
tools/api-inventory/ci/api-inventory-drift.yml
in-process 呼び出しの認可上の注意点と運用手順を文書化
  • 位置引数で呼ばれる認可付きビューの確認手順を追加
  • 台帳列の生成・検査方法とCIでの非公開出力方針を説明
tools/api-inventory/scripts/README.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@coderabbitai

coderabbitai Bot commented Sep 16, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: ca03fda3-8a1e-416b-b5cd-f81288b41428

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

API インベントリ差分(件数のみ)

台帳ブランチ: develop_v2.1.0

明細は公開できないため件数のみ表示しています。該当箇所はプライベートリポジトリ側の台帳・レポートで確認してください。

ベースラインとの差分

(生成されませんでした)

台帳との突き合わせ

(生成されませんでした)

@mhaya
mhaya merged commit ebcbb3b into develop_v2.1.0 Sep 16, 2026
158 of 160 checks passed
@mhaya
mhaya deleted the hotfix/issue62807 branch September 16, 2026 15:02

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="tools/api-inventory/scripts/audit_inprocess_views.py" line_range="205" />
<code_context>
+    except Exception:
+        return hits
+    for node in ast.walk(tree):
+        if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) \
+                and node.func.id in names and node.args:
+            hits.setdefault(node.func.id, []).append(node.lineno)
</code_context>
<issue_to_address>
**issue (broader_impact):** The AST audit only recognizes calls whose callee is a bare `ast.Name`, so calls through an imported alias (`from ... import soft_delete as delete; delete(...)`) or a module attribute (`views.soft_delete(...)`) are omitted. A future in-process caller using either form therefore passes the CI gate unnoticed, allowing an authorization decorator regression like issue62807 to return.

**Triggers:** When an in-process view caller uses an import alias or module-qualified call.

**Suggested fix:** Track import aliases and `ast.Attribute` callees when resolving imported view references.
</issue_to_address>

### Comment 2
<location path="tools/api-inventory/scripts/add_inproc_callers.py" line_range="113-115" />
<code_context>
+            if del_value.startswith("del_ver_"):
+                mock_ver.assert_called_once_with(expected_recid)
+                mock_imp.assert_not_called()
+            else:
+                mock_imp.assert_called_once_with(expected_recid)
+                mock_ver.assert_not_called()
</code_context>
<issue_to_address>
**nitpick:** An imported view is recorded as a `kwargs` in-process caller whenever no direct positional call is found, even if the imported name is never called at all. The generated `inproc_callers` inventory consequently contains false callers and can mislead authorization reviews about which non-HTTP entry points actually exist.

**Triggers:** When production code imports a routed view for reference, re-export, or another non-call use.

**Suggested fix:** Record a caller only after finding an actual call/reference invocation, and distinguish keyword calls from imports that are not invoked.
</issue_to_address>

### Comment 3
<location path=".github/workflows/api-inventory-drift.yml" line_range="118-119" />
<code_context>
+        env:
+          WEKO_API_INVENTORY_DIR: ${{ github.workspace }}/.api-inventory-data
+        run: |
+          python3 tools/api-inventory/scripts/add_inproc_callers.py \
+            --check --summary-only --gate
+
       - name: Start WEKO containers
</code_context>
<issue_to_address>
**issue (broader_impact):** The newly mandatory gate treats a missing `inproc_callers` column as an empty inventory, so the existing private ledger causes every detected in-process caller to appear as drift and exits with status 1. Until the separately coordinated private-ledger schema update and matching branch exist, the workflow blocks the PR rather than merely reporting the inventory mismatch.

**Triggers:** When the private inventory branch still contains the pre-63-column TSV, including the documented fallback to the default branch.

**Suggested fix:** Add and validate the `inproc_callers` column before enabling the gate, or explicitly detect the old schema and fail with a targeted migration message rather than treating every caller as an ordinary drift.

```suggestion
          if ! awk -F '\t' 'NR == 1 { for (i = 1; i <= NF; i++) if ($i == "inproc_callers") found = 1 } END { exit !found }' \
              "$WEKO_API_INVENTORY_DIR/weko3_api_list_full.tsv"; then
            echo "::error::台帳に inproc_callers 列がありません。プライベート台帳のスキーマを更新してからこのゲートを有効化してください。"
            exit 1
          fi
          python3 tools/api-inventory/scripts/add_inproc_callers.py \
            --check --summary-only --gate
```
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 2 findings to address first, and if positional argument binding resolves the wrong record ID or fails to enforce the intended permission check, a caller could delete a record they should not be able to access. Reverting would stop the new behavior, but any deletions that occurred would remain and require separate recovery.

Blocking findings: tools/api-inventory/scripts/audit_inprocess_views.py:205, .github/workflows/api-inventory-drift.yml:119


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

except Exception:
return hits
for node in ast.walk(tree):
if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (broader_impact): The AST audit only recognizes calls whose callee is a bare ast.Name, so calls through an imported alias (from ... import soft_delete as delete; delete(...)) or a module attribute (views.soft_delete(...)) are omitted. A future in-process caller using either form therefore passes the CI gate unnoticed, allowing an authorization decorator regression like issue62807 to return.

Triggers: When an in-process view caller uses an import alias or module-qualified call.

Suggested fix: Track import aliases and ast.Attribute callees when resolving imported view references.

Comment on lines +113 to +115
else:
idx[(v["file"], v["func"])].append(
"kwargs:{}:{}".format(importer, line))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: An imported view is recorded as a kwargs in-process caller whenever no direct positional call is found, even if the imported name is never called at all. The generated inproc_callers inventory consequently contains false callers and can mislead authorization reviews about which non-HTTP entry points actually exist.

Triggers: When production code imports a routed view for reference, re-export, or another non-call use.

Suggested fix: Record a caller only after finding an actual call/reference invocation, and distinguish keyword calls from imports that are not invoked.

Comment on lines +118 to +119
python3 tools/api-inventory/scripts/add_inproc_callers.py \
--check --summary-only --gate

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (broader_impact): The newly mandatory gate treats a missing inproc_callers column as an empty inventory, so the existing private ledger causes every detected in-process caller to appear as drift and exits with status 1. Until the separately coordinated private-ledger schema update and matching branch exist, the workflow blocks the PR rather than merely reporting the inventory mismatch.

Triggers: When the private inventory branch still contains the pre-63-column TSV, including the documented fallback to the default branch.

Suggested fix: Add and validate the inproc_callers column before enabling the gate, or explicitly detect the old schema and fail with a targeted migration message rather than treating every caller as an ordinary drift.

Suggested change
python3 tools/api-inventory/scripts/add_inproc_callers.py \
--check --summary-only --gate
if ! awk -F '\t' 'NR == 1 { for (i = 1; i <= NF; i++) if ($i == "inproc_callers") found = 1 } END { exit !found }' \
"$WEKO_API_INVENTORY_DIR/weko3_api_list_full.tsv"; then
echo "::error::台帳に inproc_callers 列がありません。プライベート台帳のスキーマを更新してからこのゲートを有効化してください。"
exit 1
fi
python3 tools/api-inventory/scripts/add_inproc_callers.py \
--check --summary-only --gate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant