Repository navigation
Develop v2.1.0 - #1938
Develop v2.1.0#1938
Conversation
…e urls - Add an identity check between the issued secret/one-time URL record (record_id, file_name) and the item/file actually being requested. - Fix the four management-operation handlers: each caught its own abort(403)/abort(404) in a broad `except Exception` and turned it into a 500 response. - Fix is_private_index() to correctly reject an item when it belongs to multiple indexes that are all non-public. - Add and update unit tests accordingly.
pip freeze の出力に、取得時に使ったユーザ名とアクセストークンが URL に埋め込まれたまま残っていた。RCOSDP/weko は public なので 認証なしで取得でき、認証情報は不要。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
d6d92a5 / 3de23b2(fix build error)で nginx/Dockerfile が旧 nginx/Dockerfile.arm64 と同じ内容に置き換わり、7e536799c で行った login.php → login.py の切り替えが失われていた。weko.conf の /secure/、 supervisord.conf の fcgiwrap、WEKO_ACCOUNTS_SHIB_IDP_LOGIN_URL は login.py を前提にしているため、このままでは Shibboleth でログインできない。 - nginx/Dockerfile, Dockerfile.arm64: fcgiwrap と python3-requests を入れ、 secure/login.py を配置する - login.php による運用は終わっているので PHP を取り除く - php-fpm / php-curl のインストールと php-fpm の設定書き換えを削除 - supervisord.conf の php-fpm を削除 - weko-ams.conf / weko-ams-restricted.conf の .php 用 location と、 weko.conf のコメントアウト済みの同 location を削除 - nginx/login.php, index.php, phpinfo.php を削除 - login.py が使う requests は pip ではなく apt の python3-requests で入れる (Dockerfile.ams の python3-pip / pip3 install requests も置き換え) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
supervisord.conf は shibauthorizer / shibresponder を /usr/lib/x86_64-linux-gnu/shibboleth/ から起動するが、arm64 では /usr/lib/aarch64-linux-gnu/shibboleth/ にあるため FATAL になり、 /secure/ の shib_request が通らず Shibboleth でログインできなかった。 supervisord.conf は Dockerfile と Dockerfile.arm64 で共有しているので、 ビルド時に uname -m でパスを書き換える。x86_64 では書き換わらない。 両 Dockerfile は同じ内容に保つ。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix issue 62892: record/file match on secret and one-time url
Feature/issue61752 alembic
- incubator-pagespeed-ngx は x86_64 用の PSOL (1.13.35.2-x64) を取り込む ため arm64 ではコンパイルできない。weko-ams.conf などでも使っていない ので、Dockerfile と同じく組み込まないようにする - ビルドした .deb を focal_amd64.deb と決め打ちせず、ワイルドカードで取る - supervisord.conf の shibauthorizer / shibresponder のパスを、Dockerfile と 同じくビルド時に uname -m で書き換える - nginx 1.18.0 時代のコメントアウト行を削除する Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
facd5ab で /widget/uploaded/<filename>[/<community_id>] (uploaded_file) に System/Repository/Community Administrator のロール必須が付いた。この URL は ウィジェットに埋め込んだ画像などを公開ページのブラウザが直接読み込むもの (WidgetBucket.save_file が返す URL) なので、未ログインでは 401、一般ユーザでは 403 になり、公開ページのウィジェット画像が表示されなくなっていた。 - uploaded_file のロール必須を外す。load_widget_type (管理画面の ウィジェット作成でだけ使う) は管理者限定のまま - test_uploaded_file が同名で2つ定義され、後の定義が前を上書きしていたので、 未ログイン・各ロールで取得できることを確かめる1つのテストにまとめる - test_load_widget_type が users[id]['obj'] でログインしていたため、 users[4] (generaluser) の obj が sysadmin になっている fixture では システム管理者としてログインし、403 の確認が失敗していた。email で ログインするように直す Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix: freeze.txt の URL から認証情報を除く
fix(nginx): Shibboleth ログインスクリプトを login.py に戻し、PHP を取り除く(arm64 対応を含む)
fix(weko-gridlayout): ウィジェットのアップロードファイルを未ログインでも取得できるようにする
Reviewer's GuideThis release-development PR combines download URL security hardening, public widget asset access, revised private-index evaluation, a read-only storage-credential migration, and migration of nginx/Shibboleth authentication from PHP-FPM to Python/fcgiwrap, with corresponding regression coverage and deployment configuration updates. Sequence diagram for download URL target validationsequenceDiagram
participant Client
participant DownloadView
participant DownloadUtils
participant Database
Client->>DownloadView: copy_secret_url(pid, record, filename)
DownloadView->>Database: FileSecretDownload.get_by_id(secret_url_id)
Database-->>DownloadView: url_record
DownloadView->>DownloadUtils: ensure_url_record_matches(url_record, pid, filename)
alt target mismatch or missing URL
DownloadView-->>Client: 404 Not Found
else target matches
DownloadView->>DownloadUtils: create_download_url(url_record)
DownloadView-->>Client: download URL
end
Client->>DownloadUtils: validate_url_download(record, filename, token)
DownloadUtils->>DownloadUtils: convert_token_into_obj(token, is_secret_url)
DownloadUtils->>DownloadUtils: ensure_url_record_matches(url_obj, record_id, filename)
alt token does not match requested file
DownloadUtils-->>Client: unavailable response
else token matches
DownloadUtils-->>Client: continue download validation
end
Entity relationship diagram for read-only file credentialserDiagram
FILES_LOCATION {
string readonly_access_key
string readonly_secret_key
}
Flow diagram for private-index evaluationflowchart TD
Start["Record supplied to is_private_index"] --> Path["Read record path"]
Path --> Empty{path is empty}
Empty -->|yes| Private["Return True"]
Empty -->|no| PublicCheck["Indexes.is_public_state_and_not_in_future(list_index)"]
PublicCheck --> Result["Return not public state result"]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
🔍 Claude レビュー統合Claude の追加指摘 4 件 — 🔴 高 4 / 🟠 中 0 / 🟡 低 0
1. 🔴 [高] down_revision が存在しないリビジョンを指しており、alembic のチェーンが壊れる(Claude の追加指摘)
down_revision に指定された '8644b32a3eec' は、リポジトリ内のどのマイグレーションにも定義されていない。alembic は KeyError(Can't locate revision)で失敗するか、このファイルが別のブランチ起点になってしまう。そのため 修正案 他の system で別ブランチ由来の 8644b32a3eec が必要な場合は、そのリビジョンのファイルも追加すること。 根拠確認: alembic ディレクトリを ls し、revision / down_revision を grep した。現在の最新リビジョンは 8ae99b034410(down_revision = f741aa746a7d)。'8644b32a3eec' はリポジトリ全体の *.py でこのファイルにしか出てこない。 2. 🔴 [高] 削除した GitHub PAT は履歴に残っているため、失効が必要(Claude の追加指摘)
このコミットで freeze.txt から ghp_ のトークンを消しても、git 履歴・フォーク・PR の差分には残る。トークンが生きている限り漏洩状態が続く。 修正案 GitHub 側で該当 PAT(mhaya アカウント)を直ちに失効させる。必要なら履歴の書き換えも検討する。 根拠確認: 差分の削除行を確認した。トークンが失効済みかどうかは確認できていない。 3. 🔴 [高] マイグレーションの down_revision が存在しないリビジョンを指している(Claude の追加指摘)
down_revision = '8644b32a3eec' に対応するリビジョンが alembic ディレクトリにない。このため alembic のリビジョングラフが壊れ、 修正案 他ブランチで 8644b32a3eec が別途定義される予定なら、そのファイルをこの PR に含めること。 根拠確認: alembic ディレクトリのファイル一覧と down_revision を grep し、8644b32a3eec が modules 配下のどこにも revision として定義されていないことを確認した。チェーンは 52ce868f33c3→2e97565eba72→f741aa746a7d→8ae99b034410 である。 4. 🔴 [高] 漏洩済み GitHub PAT の失効が必要(Claude の追加指摘)
この変更で freeze.txt から ghp_ トークンは消えるが、git 履歴には残る。履歴を書き換えない限り、トークンは有効なまま取得可能である。 修正案 GitHub 側で該当トークンを直ちに revoke する。 根拠確認: 差分の内容のみで判断した。トークンが失効済みかどうかはリポジトリからは確認できない。 次にすること: alembic マイグレーションの down_revision が実在せず(現在のヘッドは 8ae99b034410)、DB マイグレーションが壊れる。8ae99b034410 に直すこと。また freeze.txt から削除した PAT は履歴に残っているので、GitHub 側で失効させること。他の変更(URL とファイル名の照合、uploaded_file の認可緩和、is_private_index の委譲先の存在)は、読んだ範囲では問題なかった。 モデル sonnet / 2 回実行して和集合 / コスト $0.6078。同じ入力でも結果が揺れるため複数回まわし、一部のパスでしか挙がらなかったものには回数を添えています 他レビューを踏まえた自動レビューです。誤りが含まれることがあります。 |
There was a problem hiding this comment.
Hey - I've found 7 issues
Fixed security issues:
- freeze.txt (link)
- SSL verification is disabled but should not be (currently CURLOPT_SSL_VERIFYPEER= false) (link)
- The 'phpinfo' function may reveal sensitive information about your environment. (link)
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="modules/weko-gridlayout/weko_gridlayout/views.py" line_range="614-616" />
<code_context>
@blueprint.route('/widget/uploaded/<string:filename>/<string:community_id>',
methods=["GET"]
)
-@roles_required([WEKO_ADMIN_PERMISSION_ROLE_SYSTEM,
- WEKO_ADMIN_PERMISSION_ROLE_REPO,
- WEKO_ADMIN_PERMISSION_ROLE_COMMUNITY])
def uploaded_file(filename, community_id=0):
"""Get widget static file.
</code_context>
<issue_to_address>
**Private widget files are exposed**
When a private or unpublished widget file exists and an unauthenticated caller knows or guesses its filename and community ID, `uploaded_file()` passes the request to `WidgetBucket.get_file()` without checking community visibility or caller access, so the caller retrieves the private file without logging in.
Restore checks for community visibility and caller authorization before serving a widget file.
</issue_to_address>
### Comment 2
<location path="modules/invenio-files-rest/invenio_files_rest/alembic/faf4faa08601_add_column_file_readonly_keys.py" line_range="16" />
<code_context>
+
+# revision identifiers, used by Alembic.
+revision = 'faf4faa08601'
+down_revision = '8644b32a3eec'
+branch_labels = ()
+depends_on = None
</code_context>
<issue_to_address>
**Database upgrades cannot load**
When alembic loads the registered migration graph for an upgrade or downgrade, alembic cannot resolve `down_revision` `8644b32a3eec` because no migration with that revision exists, so the migration graph fails to load and database upgrades cannot run.
Set `down_revision` to the actual existing parent revision for this migration.
</issue_to_address>
### Comment 3
<location path="nginx/Dockerfile" line_range="88-89" />
<code_context>
-#ADD ./phpinfo.php /usr/share/nginx/html/secure/phpinfo.php
-#ADD ./index.php /usr/share/nginx/html/secure/index.php
-ADD ./login.php /usr/share/nginx/html/secure/login.php
+ADD ./login.py /usr/share/nginx/html/secure/login.py
+RUN chmod 755 /usr/share/nginx/html/secure/login.py
ADD ./shib_clear_headers /etc/nginx/shib_clear_headers
</code_context>
<issue_to_address>
**Shibboleth login requests fail**
When a request reaches `login.py` and it returns a redirect or login page, `login.py` emits an HTTP status line as its first CGI output line instead of a valid CGI header. `fcgiwrap` passes it to Nginx as a FastCGI response header, which Nginx rejects and the login request fails.
Emit a valid CGI response header, such as `Status: 302 Found` or `Status: 200 OK`, instead of an HTTP status line.
</issue_to_address>
### Comment 4
<location path="nginx/Dockerfile" line_range="88-89" />
<code_context>
-#ADD ./phpinfo.php /usr/share/nginx/html/secure/phpinfo.php
-#ADD ./index.php /usr/share/nginx/html/secure/index.php
-ADD ./login.php /usr/share/nginx/html/secure/login.php
+ADD ./login.py /usr/share/nginx/html/secure/login.py
+RUN chmod 755 /usr/share/nginx/html/secure/login.py
ADD ./shib_clear_headers /etc/nginx/shib_clear_headers
</code_context>
<issue_to_address>
**Login flow permits external redirects**
When a victim follows a Shibboleth login link whose `next` parameter names an external URL, `login.py` takes `next` from the request query and forwards it to `/weko/shib/login`; the login flow stores that value and later redirects to it. An attacker can send a victim to the Shibboleth login endpoint with an attacker-controlled `next`, then land the victim on a phishing site after login.
Accept only validated same-origin relative destinations for `next` and reject external URLs before storing or redirecting to them.
</issue_to_address>
### Comment 5
<location path="nginx/Dockerfile" line_range="88" />
<code_context>
-#ADD ./phpinfo.php /usr/share/nginx/html/secure/phpinfo.php
-#ADD ./index.php /usr/share/nginx/html/secure/index.php
-ADD ./login.php /usr/share/nginx/html/secure/login.php
+ADD ./login.py /usr/share/nginx/html/secure/login.py
+RUN chmod 755 /usr/share/nginx/html/secure/login.py
</code_context>
<issue_to_address>
**Temporary cookie files accumulate**
When the login handler processes repeated requests over the container's lifetime, `login.py` creates a named temporary cookie file for each login and never removes it, so files accumulate in `/tmp` until the container runs out of filesystem space or inodes and login requests fail.
Remove each temporary cookie file after it is no longer needed, including on error paths.
</issue_to_address>
### Comment 6
<location path="modules/weko-records-ui/weko_records_ui/utils.py" line_range="2394" />
<code_context>
+
+ # Check that the token was actually issued for the requested item/file.
+ # The same generic message as the file-access check above is used.
+ if not ensure_url_record_matches(url_obj, record.get('recid'), filename):
+ return False, _('This file is currently not available for this feature.')
+
</code_context>
<issue_to_address>
**Download limits can be exceeded**
When two requests for the same URL validate concurrently while its download count is below the limit, `validate_url_download()` lets both requests pass before `process_onetime_file_download()` or `file_download_secret` increments the count. Both requests can deliver the file, and the read-modify-write increments can persist only one count, leaving the URL usable after its limit is exceeded.
Atomically check and reserve a download against the limit before serving the file.
</issue_to_address>
### Comment 7
<location path="nginx/Dockerfile" line_range="88" />
<code_context>
-#ADD ./phpinfo.php /usr/share/nginx/html/secure/phpinfo.php
-#ADD ./index.php /usr/share/nginx/html/secure/index.php
-ADD ./login.php /usr/share/nginx/html/secure/login.php
+ADD ./login.py /usr/share/nginx/html/secure/login.py
+RUN chmod 755 /usr/share/nginx/html/secure/login.py
</code_context>
<issue_to_address>
**Login drops destination query parameters**
When a user starts Shibboleth login from a destination URL containing `&`-separated query parameters, `login.py` parses `next` and concatenates its decoded value into the internal `/weko/shib/login` URL without re-encoding it. Any `&` in the destination becomes a separate query parameter, so the post-login redirect loses the remaining destination parameters and links that depend on them fail.
URL-encode `next` when constructing the internal login URL, and use `/` as the unencoded fallback value.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 7 findings to address first, and removing the role check makes every widget-uploaded file reachable anonymously, so a mistaken access-policy decision could expose the full existing widget-file set immediately and expose files during the window cannot be undone by reverting. The URL-binding fixes and the Nginx/Shibboleth runtime migration also change security and production behavior, with rollback restoring code/config but not undoing any access or outages that already occurred.
Blocking findings: modules/weko-gridlayout/weko_gridlayout/views.py:616, modules/invenio-files-rest/invenio_files_rest/alembic/faf4faa08601_add_column_file_readonly_keys.py:16, nginx/Dockerfile:89, nginx/Dockerfile:89, nginx/Dockerfile:88, and 2 more
| @roles_required([WEKO_ADMIN_PERMISSION_ROLE_SYSTEM, | ||
| WEKO_ADMIN_PERMISSION_ROLE_REPO, | ||
| WEKO_ADMIN_PERMISSION_ROLE_COMMUNITY]) |
There was a problem hiding this comment.
🟠 High · Private widget files are exposed
When a private or unpublished widget file exists and an unauthenticated caller knows or guesses its filename and community ID, uploaded_file() passes the request to WidgetBucket.get_file() without checking community visibility or caller access, so the caller retrieves the private file without logging in.
Restore checks for community visibility and caller authorization before serving a widget file.
Prompt for AI agents
In `modules/weko-gridlayout/weko_gridlayout/views.py` at lines 614-616:
**Private widget files are exposed**
When a private or unpublished widget file exists and an unauthenticated caller knows or guesses its filename and community ID, `uploaded_file()` passes the request to `WidgetBucket.get_file()` without checking community visibility or caller access, so the caller retrieves the private file without logging in.
Restore checks for community visibility and caller authorization before serving a widget file.|
|
||
| # revision identifiers, used by Alembic. | ||
| revision = 'faf4faa08601' | ||
| down_revision = '8644b32a3eec' |
There was a problem hiding this comment.
🔴 Critical · Database upgrades cannot load
When alembic loads the registered migration graph for an upgrade or downgrade, alembic cannot resolve down_revision 8644b32a3eec because no migration with that revision exists, so the migration graph fails to load and database upgrades cannot run.
Set down_revision to the actual existing parent revision for this migration.
Prompt for AI agents
In `modules/invenio-files-rest/invenio_files_rest/alembic/faf4faa08601_add_column_file_readonly_keys.py` at line 16:
**Database upgrades cannot load**
When alembic loads the registered migration graph for an upgrade or downgrade, alembic cannot resolve `down_revision` `8644b32a3eec` because no migration with that revision exists, so the migration graph fails to load and database upgrades cannot run.
Set `down_revision` to the actual existing parent revision for this migration.| ADD ./login.py /usr/share/nginx/html/secure/login.py | ||
| RUN chmod 755 /usr/share/nginx/html/secure/login.py |
There was a problem hiding this comment.
🟠 High · Shibboleth login requests fail
When a request reaches login.py and it returns a redirect or login page, login.py emits an HTTP status line as its first CGI output line instead of a valid CGI header. fcgiwrap passes it to Nginx as a FastCGI response header, which Nginx rejects and the login request fails.
Emit a valid CGI response header, such as Status: 302 Found or Status: 200 OK, instead of an HTTP status line.
Prompt for AI agents
In `nginx/Dockerfile` at lines 88-89:
**Shibboleth login requests fail**
When a request reaches `login.py` and it returns a redirect or login page, `login.py` emits an HTTP status line as its first CGI output line instead of a valid CGI header. `fcgiwrap` passes it to Nginx as a FastCGI response header, which Nginx rejects and the login request fails.
Emit a valid CGI response header, such as `Status: 302 Found` or `Status: 200 OK`, instead of an HTTP status line.| ADD ./login.py /usr/share/nginx/html/secure/login.py | ||
| RUN chmod 755 /usr/share/nginx/html/secure/login.py |
There was a problem hiding this comment.
🟡 Medium · Login flow permits external redirects
When a victim follows a Shibboleth login link whose next parameter names an external URL, login.py takes next from the request query and forwards it to /weko/shib/login; the login flow stores that value and later redirects to it. An attacker can send a victim to the Shibboleth login endpoint with an attacker-controlled next, then land the victim on a phishing site after login.
Accept only validated same-origin relative destinations for next and reject external URLs before storing or redirecting to them.
Prompt for AI agents
In `nginx/Dockerfile` at lines 88-89:
**Login flow permits external redirects**
When a victim follows a Shibboleth login link whose `next` parameter names an external URL, `login.py` takes `next` from the request query and forwards it to `/weko/shib/login`; the login flow stores that value and later redirects to it. An attacker can send a victim to the Shibboleth login endpoint with an attacker-controlled `next`, then land the victim on a phishing site after login.
Accept only validated same-origin relative destinations for `next` and reject external URLs before storing or redirecting to them.| #ADD ./phpinfo.php /usr/share/nginx/html/secure/phpinfo.php | ||
| #ADD ./index.php /usr/share/nginx/html/secure/index.php | ||
| ADD ./login.php /usr/share/nginx/html/secure/login.php | ||
| ADD ./login.py /usr/share/nginx/html/secure/login.py |
There was a problem hiding this comment.
🟡 Medium · Temporary cookie files accumulate
When the login handler processes repeated requests over the container's lifetime, login.py creates a named temporary cookie file for each login and never removes it, so files accumulate in /tmp until the container runs out of filesystem space or inodes and login requests fail.
Remove each temporary cookie file after it is no longer needed, including on error paths.
Prompt for AI agents
In `nginx/Dockerfile` at line 88:
**Temporary cookie files accumulate**
When the login handler processes repeated requests over the container's lifetime, `login.py` creates a named temporary cookie file for each login and never removes it, so files accumulate in `/tmp` until the container runs out of filesystem space or inodes and login requests fail.
Remove each temporary cookie file after it is no longer needed, including on error paths.|
|
||
| # Check that the token was actually issued for the requested item/file. | ||
| # The same generic message as the file-access check above is used. | ||
| if not ensure_url_record_matches(url_obj, record.get('recid'), filename): |
There was a problem hiding this comment.
🟠 High · Download limits can be exceeded
When two requests for the same URL validate concurrently while its download count is below the limit, validate_url_download() lets both requests pass before process_onetime_file_download() or file_download_secret increments the count. Both requests can deliver the file, and the read-modify-write increments can persist only one count, leaving the URL usable after its limit is exceeded.
Atomically check and reserve a download against the limit before serving the file.
Prompt for AI agents
In `modules/weko-records-ui/weko_records_ui/utils.py` at line 2394:
**Download limits can be exceeded**
When two requests for the same URL validate concurrently while its download count is below the limit, `validate_url_download()` lets both requests pass before `process_onetime_file_download()` or `file_download_secret` increments the count. Both requests can deliver the file, and the read-modify-write increments can persist only one count, leaving the URL usable after its limit is exceeded.
Atomically check and reserve a download against the limit before serving the file.| #ADD ./phpinfo.php /usr/share/nginx/html/secure/phpinfo.php | ||
| #ADD ./index.php /usr/share/nginx/html/secure/index.php | ||
| ADD ./login.php /usr/share/nginx/html/secure/login.php | ||
| ADD ./login.py /usr/share/nginx/html/secure/login.py |
There was a problem hiding this comment.
🟡 Medium · Login drops destination query parameters
When a user starts Shibboleth login from a destination URL containing &-separated query parameters, login.py parses next and concatenates its decoded value into the internal /weko/shib/login URL without re-encoding it. Any & in the destination becomes a separate query parameter, so the post-login redirect loses the remaining destination parameters and links that depend on them fail.
URL-encode next when constructing the internal login URL, and use / as the unencoded fallback value.
Prompt for AI agents
In `nginx/Dockerfile` at line 88:
**Login drops destination query parameters**
When a user starts Shibboleth login from a destination URL containing `&`-separated query parameters, `login.py` parses `next` and concatenates its decoded value into the internal `/weko/shib/login` URL without re-encoding it. Any `&` in the destination becomes a separate query parameter, so the post-login redirect loses the remaining destination parameters and links that depend on them fail.
URL-encode `next` when constructing the internal login URL, and use `/` as the unencoded fallback value.
Ooka-Masashi
left a comment
There was a problem hiding this comment.
release v2.1.0
API インベントリ差分(件数のみ)
ベースラインとの差分(生成されませんでした) 台帳との突き合わせ(生成されませんでした) ソース由来の経路検知(生成されませんでした) |
概要 (Summary)
関連Issue / チケット (Related Issues)
変更タイプ (Type of Change)
🤖 0. CI 自動チェック (API Inventory Drift)
PR ごとに WEKO3 コンテナを起動し、
url_mapのダンプ・台帳との突き合わせ・変更行の到達可否測定を自動実行する。結果は PR コメントと Actions の artifact
(
api-inventory-summary) に出る。このリポジトリは public のため、台帳もベースラインも同梱していない。
実データはプライベートリポジトリ
RCOSDP/weko-secretにあり、CI は Secret 経由で取得する。以降この文書では、そこを単にプライベートリポジトリと呼ぶ。
台帳はブランチごとに内容が違うため、CI は weko 側と同名のブランチを
プライベートリポジトリから探して使う(head → base → 既定ブランチ の順)。
採用されたブランチ名は PR コメントの冒頭に出るので、件数を読む前にそこを見ること。
対応ブランチが無い場合は既定ブランチと比較され、コメント冒頭に警告が出る。
その件数は当てにならないので、PASS でも「確認済み」と読まないこと。
詳細:
tools/api-inventory/ci/README.md§3aSecret (
API_INVENTORY_REPO/API_INVENTORY_SSH_KEY) が未設定のリポジトリ、および fork からの PR では、このジョブは何もせずスキップされる。
API を追加・変更した場合(必須)
この PR が
fix/issue62569→develop_v2.0.4なら、プライベート側もfix/issue62569→develop_v2.0.4。同名にしておけば台帳 PR が未マージでもCI がそれを見るので、2つの PR のマージ順を気にしなくてよい。
api_snapshot.jsonを更新し、対応する PR を出したbash export WEKO_API_INVENTORY_DIR=/path/to/weko-secret ./install.sh python3 tools/api-inventory/scripts/snapshot.py --out "$WEKO_API_INVENTORY_DIR/api_snapshot.json"更新しないと CI が落ちる。公開リポジトリのコード変更とは別の PRになる。
weko3_api_list_full.tsvに行を追加・更新し、build_checklist.pyで 24 列版を再生成した(未収載だと reconcile が FAIL する)(
git statusに*.tsv/api_snapshot.jsonが出ていないこと)FAIL したときの対処(要約)
まず PR コメント冒頭の台帳ブランチを見る。警告が出ていれば、件数を追う前に
プライベートリポジトリ側の対応ブランチを用意すること(比較相手が違うので件数に意味がない)。
ジョブが落ちる条件は 3 つある。PR コメントのどのセクションに件数が出ているかで切り分ける。
drift.md)reconcile.md)*_PERMISSION_FACTORY/ CSRF 保護 等が危険側の値に変わったcan_delete/can_exportがFalse→Truedata_opを更新data_opが作成/更新/削除の経路に、未認証で到達したdata_opの記載誤りなら台帳を直すurl_mapに無いreconcile B のうち、実機に存在しないことが正当な行(プラグイン未登録・config で無効等)は
プライベートリポジトリの
reconcile_allow.jsonに理由付きで登録する。理由なしの登録は不可。登録済みの行は B'(既知・許容)として集計され、E'(endpoint が実機に無い)と併せてゲート対象外になる。
W1〜W6 は WARN でゲートは通るが、レビューでは見ること
(ModelView の追加 / 実装本体の変化 / HTTP メソッド・URL の変化 / 監視対象 config の変化 /
依存パッケージの版の変化)。特に W6(依存の版)は、ベースラインを CI と異なる環境で作ると
毎回出続けて形骸化するため、ベースラインは
install.shで作った環境から生成する。🔒 1. セキュリティ & API アクセス制御チェック (必須)
認証・認可 (Authentication & Authorization)
@login_required,@pass_record,need(...), Invenio Access Action/api/*ではPermission.require(http_exception=403)を使うこと。@login_requiredは API アプリにsecurity.loginが無いため 401 ではなく 500 になる--allow-writes付きでGET / HEAD 以外も叩く)
起動した経路のみ。ワークフロー系など未解決プレースホルダの行は skip される
Noneで無効化していない*_PERMISSION_FACTORY等を監視機能クローズ・非公開化の場合 (Feature Disable)
404 Not Foundまたは403 Forbiddenが返ることを確認した🧪 2. テストコード観点チェック (pytest / Invenio Test Suite)
権限・異常系テスト (Negative & Authorization Tests)
401 Unauthorizedまたは403 Forbidden/404 Not Foundが返ることを検証するテストがある403になるテストがある404/403を返すテストがある境界値・入力バリデーションテスト (Boundary & Validation)
400 Bad Request/ バリデーションエラーが返るテストがあるデータ整合性・トランザクションテスト (Integrity & Rollback)
🛡️ 3. データ保護 & 破壊的変更防止チェック (Data Safety)
⚙️ 4. マイグレーション & システム影響チェック (Invenio / WEKO3 Stack)
データベース (DB / Alembic)
invenio alembic upgrade(適用)およびdowngrade(ロールバック)スクリプトを作成・検証した検索インデックス (Elasticsearch / OpenSearch)
設定 & 非同期処理 (Config / Celery / Cache)
invenio.cfg/ 環境変数のデフォルト値を設定した📚 5. ドキュメント・仕様書更新チェック (weko-document)
tools/api-inventory/、台帳・調査記録はプライベートリポジトリ(public リポジトリには置かない)。
§0 のチェック項目で対応済みなら、ここは確認のみ。
weko3_api_auth_findings.md)もプライベートリポジトリに置く。台帳は二重管理しない📋 6. 動作検証エビデンス (Verification Evidence)
テスト実行結果
CI の成果物 (artifact:
api-inventory-summary)drift.mdreconcile.md明細(該当した経路名・実測結果)は公開できないため artifact に含めていない。
プライベートリポジトリ側で同じコマンドを
--summary-onlyなしで実行して確認する。手動で確認したこと
Summary by Sourcery
Harden download and index access behavior while migrating restricted Nginx authentication from PHP to Python.
New Features:
Bug Fixes:
Enhancements:
Build:
Deployment:
Tests:
Chores: