Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 71 additions & 7 deletions tools/api-inventory/scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,27 +57,89 @@ python3 tools/api-inventory/scripts/prioritize.py # 優先度・整理対
python3 tools/api-inventory/scripts/build_checklist.py # 24列版を再生成
```

## ケース2: 台帳に行を追加・修正する
## ケース2: 台帳に行を追加する

`reconcile.py` が「A. インベントリ未収載」を出したときなど
`reconcile.py` が「A. インベントリ未収載」を出したとき。57列を手で並べる必要はない

```bash
# 1) full.tsv を直接編集(65列。列数を合わせること)
# 1) 何が未収載かを確認する
python3 tools/api-inventory/scripts/reconcile.py
# → A. インベントリ未収載 に endpoint 名が出る

# 2) 雛形を確認する(まだ書き込まない)
python3 tools/api-inventory/scripts/add_row.py --endpoint api:weko_admin.foo
# URI の一部でも探せる: --uri /api/items/import-task

# 3) 追記する
python3 tools/api-inventory/scripts/add_row.py --endpoint api:weko_admin.foo --append

# 4) TODO の列を埋める(下記)
vi "$WEKO_API_INVENTORY_DIR/weko3_api_list_full.tsv"

# 2) 列数の検算
# 5) 列数の検算
awk -F'\t' 'NR>1 && NF!=65{print "行"NR" 列数="NF}' \
"$WEKO_API_INVENTORY_DIR/weko3_api_list_full.tsv"

# 3) 派生列を再計算 → 24列版を再生成
# 6) 派生列を再計算 → 24列版を再生成 → 突き合わせ
python3 tools/api-inventory/scripts/test_coverage.py
python3 tools/api-inventory/scripts/prioritize.py
python3 tools/api-inventory/scripts/build_checklist.py
python3 tools/api-inventory/scripts/reconcile.py --gate # 差分0になること
```

### 機械付与スクリプトで TODO を減らす

`add_row.py --append` の直後に、Phase 2 の機械付与スクリプトを流すと `TODO` が減る。

# 4) 実機と一致するか確認(差分0になること)
python3 tools/api-inventory/scripts/reconcile.py --gate
```bash
export WEKO_ROOT=/path/to/weko # 解析対象のソース
python3 tools/api-inventory/scripts/add_cols.py # csrf_protection / input_validation /
# audit_logged / triggers_task / resource_limit
python3 tools/api-inventory/scripts/add_ssrf_redirect.py # redirect_target / ssrf_surface
python3 tools/api-inventory/scripts/add_idempotency.py # idempotency
python3 tools/api-inventory/scripts/add_dataop4.py # data_op_detail
python3 tools/api-inventory/scripts/add_authmech.py # auth_mechanism / bola_risk
```

**これらは空欄/`TODO` のセルだけを埋める。既存値は上書きしない。**
台帳の既存値は機械出力そのままではなく後から精査されており、一括再生成すると劣化する
(実測: `bola_risk` の判定が逆転、`data_op_detail` の論理削除/物理削除の区別が失われる、
`csrf_protection` の指摘が消える)。意図して作り直すときだけ
`WEKO_INVENTORY_OVERWRITE=1` を付ける。

### add_row.py が埋める列 / 埋めない列

`api_snapshot.json`(実機 url_map)と git から**機械的に決まる27列**を埋め、
調査が要る31列に `TODO` を入れる。

| 自動(27列) | no / module / api_type / app / method / uri / path_params / blueprint / endpoint / impl_func / impl_file / impl_line / auth_required / auth_method / auth_mechanism / api_version / last_commit系4列 ほか |
|---|---|
| **`TODO`(31列)** | **summary / response / status_codes / exceptions / roles / auth_response_variance / restricted_content / data_op / data_target / data_store / side_effects / config_deps / test_file / category_tags / notes / sec_* / dynamic_verified / csrf_protection / input_validation / audit_logged / triggers_task / resource_limit / redirect_target / ssrf_surface / idempotency / data_op_detail / bola_risk** |

`TODO` は **ソースを読まないと書けない列**。Phase 2(静的解析)と Phase 3(実機実測)で
やっていることを、その1行について行う。埋め方は列定義 README(秘密側の
`weko3_api_list_full_README.md`)の各列の説明に従う。

派生列(`priority` / `test_*` / `cleanup`)は空のままでよい。手順6で自動的に付く。

**`TODO` を残したままにしない。** 残っていると優先度判定の入力が欠けるため、
`prioritize.py` が誤った区分を付ける(例: `data_op` が `TODO` だと破壊系の判定に入らない)。
調査が終わるまでは、少なくとも `data_op` / `auth_required` / `dynamic_verified` を
埋めること。

## ケース2b: 既存行を修正する

```bash
vi "$WEKO_API_INVENTORY_DIR/weko3_api_list_full.tsv" # 本体列(1-57)だけを直す
python3 tools/api-inventory/scripts/test_coverage.py
python3 tools/api-inventory/scripts/prioritize.py
python3 tools/api-inventory/scripts/build_checklist.py
```

派生列(58-65)は手で直しても次の実行で消える。優先度を変えたいときは、
判定の入力側(`security_finding` / `dynamic_verified` / `data_op` / `deprecated`)を
直すか、`prioritize.py` のルールを変える。

## ケース3: WEKO3 のバージョンアップに伴う全面更新

```bash
Expand Down Expand Up @@ -113,6 +175,8 @@ git push origin main --follow-tags
| `test_coverage.py` | full.tsv + テストコード | full.tsv の 60-64列 |
| `prioritize.py` | full.tsv | full.tsv の 58-59, 65列 + 末尾列順の正規化 |
| `build_checklist.py` | full.tsv | **`weko3_api_list.tsv` を全体再生成** |
| `add_row.py` | `api_snapshot.json` + git | full.tsv に新規行の雛形を追記(`--append`) |
| `add_cols.py` / `add_ssrf_redirect.py` / `add_idempotency.py` / `add_dataop4.py` / `add_authmech.py` | full.tsv + 実装ソース | full.tsv の**空欄/TODO セルのみ**を機械付与 |

`test_coverage.py` → `prioritize.py` → `build_checklist.py` は**何度流しても結果が変わらない**
(冪等)。24列版は full.tsv から完全に再現できることを確認済み。
Expand Down
50 changes: 46 additions & 4 deletions tools/api-inventory/scripts/add_authmech.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,52 @@
# -*- coding: utf-8 -*-
"""auth_mechanism(認証の付け方3分類) と bola_risk(object-level認可の有無) を付与"""
import ast,re,os
R="/home/mhaya/wekov2/"
import os as _os, sys as _sys
_sys.path.insert(0, _os.path.dirname(_os.path.abspath(__file__)))
from paths import data_path as _data_path

# 入出力: 既定は $WEKO_API_INVENTORY_DIR/weko3_api_list_full.tsv を in-place 更新。
# 以前は R+"weko3_api_list.tsv"(24列版)を読み書きしており、いま実行すると
# 台帳を壊す状態だった(当時これが作業用中間ファイルだったころの名残)。
TSV = _sys.argv[1] if len(_sys.argv) > 1 else _data_path("weko3_api_list_full.tsv")


def _write(path, hd, data, newcols):
"""既存の同名列は **その位置のまま値を差し替える**。無い列だけ末尾に足す。

末尾に付け直すと列順が変わり、README の awk 例や他スクリプトの
列位置前提(c[13]=impl_file 等)が壊れる。
"""
pos = {n: i for i, n in enumerate(hd)}
add_cols = [n for n in newcols if n not in pos]
out_hd = list(hd) + add_cols
force = _os.environ.get("WEKO_INVENTORY_OVERWRITE") == "1"
empty = ("", "-", "TODO")
lines = ["\t".join(out_hd)]
filled = 0
for c in data:
row = list(c) + [""] * (len(hd) - len(c))
vals = row[len(hd):] # このスクリプトが今回算出した値
body = row[:len(hd)]
for n, v in zip(newcols, vals):
if n not in pos:
continue
cur = body[pos[n]]
# 既存値は人手で精査されている。空欄/TODO のセルだけ埋める。
# 一括再生成すると判定が劣化する(bola_risk が逆転、data_op_detail の
# 論理/物理の区別が失われる、CSRF の指摘が消える等を実測で確認済み)。
if cur in empty or force:
if cur != v:
filled += 1
body[pos[n]] = v
extra = [v for n, v in zip(newcols, vals) if n not in pos]
lines.append("\t".join(str(x).replace("\t", " ") for x in body + extra))
open(path, "w", encoding="utf-8").write("\n".join(lines) + "\n")
print(f" → 空欄/TODO を埋めたセル: {filled}"
+ (" (WEKO_INVENTORY_OVERWRITE=1 のため既存値も上書き)" if force else ""))
R = _os.environ.get("WEKO_ROOT", "/home/mhaya/wekov2") + "/"
def load(p): return [l.rstrip("\n").split("\t") for l in open(p,encoding="utf-8") if l.rstrip("\n")]
rows=load(R+"weko3_api_list.tsv"); hd=rows[0]; data=rows[1:]
rows=load(TSV); hd=rows[0]; data=rows[1:]

filecache={}
def get_file(fp):
Expand Down Expand Up @@ -76,8 +119,7 @@ def col_bola(c,seg):
c += [mech,bola]
mc[mech.split("(")[0]]+=1; bc[bola.split("(")[0]]+=1
if "★" in bola: nb+=1
open(R+"weko3_api_list.tsv","w",encoding="utf-8").write("\t".join(hd+["auth_mechanism","bola_risk"])+"\n"+
"\n".join("\t".join(str(x).replace("\t"," ") for x in c) for c in data)+"\n")
_write(TSV, hd, data, ['auth_mechanism', 'bola_risk'])
print("=== auth_mechanism 分布 ==="); [print(f" {n:4d} {k}") for k,n in mc.most_common()]
print("=== bola_risk 分布 ==="); [print(f" {n:4d} {k}") for k,n in bc.most_common()]
print("列数:",len(hd)+2)
52 changes: 47 additions & 5 deletions tools/api-inventory/scripts/add_cols.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,52 @@
# -*- coding: utf-8 -*-
"""3観点列(csrf_protection/input_validation/audit_logged/triggers_task/resource_limit)をAST+実装から機械付与"""
import ast,re,os,collections
R="/home/mhaya/wekov2/"
import os as _os, sys as _sys
_sys.path.insert(0, _os.path.dirname(_os.path.abspath(__file__)))
from paths import data_path as _data_path

# 入出力: 既定は $WEKO_API_INVENTORY_DIR/weko3_api_list_full.tsv を in-place 更新。
# 以前は R+"weko3_api_list.tsv"(24列版)を読み書きしており、いま実行すると
# 台帳を壊す状態だった(当時これが作業用中間ファイルだったころの名残)。
TSV = _sys.argv[1] if len(_sys.argv) > 1 else _data_path("weko3_api_list_full.tsv")


def _write(path, hd, data, newcols):
"""既存の同名列は **その位置のまま値を差し替える**。無い列だけ末尾に足す。

末尾に付け直すと列順が変わり、README の awk 例や他スクリプトの
列位置前提(c[13]=impl_file 等)が壊れる。
"""
pos = {n: i for i, n in enumerate(hd)}
add_cols = [n for n in newcols if n not in pos]
out_hd = list(hd) + add_cols
force = _os.environ.get("WEKO_INVENTORY_OVERWRITE") == "1"
empty = ("", "-", "TODO")
lines = ["\t".join(out_hd)]
filled = 0
for c in data:
row = list(c) + [""] * (len(hd) - len(c))
vals = row[len(hd):] # このスクリプトが今回算出した値
body = row[:len(hd)]
for n, v in zip(newcols, vals):
if n not in pos:
continue
cur = body[pos[n]]
# 既存値は人手で精査されている。空欄/TODO のセルだけ埋める。
# 一括再生成すると判定が劣化する(bola_risk が逆転、data_op_detail の
# 論理/物理の区別が失われる、CSRF の指摘が消える等を実測で確認済み)。
if cur in empty or force:
if cur != v:
filled += 1
body[pos[n]] = v
extra = [v for n, v in zip(newcols, vals) if n not in pos]
lines.append("\t".join(str(x).replace("\t", " ") for x in body + extra))
open(path, "w", encoding="utf-8").write("\n".join(lines) + "\n")
print(f" → 空欄/TODO を埋めたセル: {filled}"
+ (" (WEKO_INVENTORY_OVERWRITE=1 のため既存値も上書き)" if force else ""))
R = _os.environ.get("WEKO_ROOT", "/home/mhaya/wekov2") + "/"
def load(p): return [l.rstrip("\n").split("\t") for l in open(p,encoding="utf-8") if l.rstrip("\n")]
rows=load(R+"weko3_api_list.tsv"); hd=rows[0]; data=rows[1:]
rows=load(TSV); hd=rows[0]; data=rows[1:]

# 実装関数のソース断片をキャッシュ
srccache={}
Expand Down Expand Up @@ -68,8 +111,7 @@ def col_reslimit(seg):
for c in data:
seg=get_src(c[13],c[14]) if c[14].isdigit() else ""
c += [col_csrf(c,seg), col_input(seg), col_audit(seg), col_task(seg), col_reslimit(seg)]
open(R+"weko3_api_list.tsv","w",encoding="utf-8").write("\t".join(hd+newcols)+"\n"+
"\n".join("\t".join(x.replace("\t"," ") for x in c) for c in data)+"\n")
_write(TSV, hd, data, ['csrf_protection', 'input_validation', 'audit_logged', 'triggers_task', 'resource_limit'])
# サマリ
for i,name in enumerate(newcols):
ci=len(hd)+i
Expand All @@ -78,4 +120,4 @@ def col_reslimit(seg):
v=c[ci] if len(c)>ci else "-"
cnt["有" if v not in("-","N/A(参照系)","CSRF該当外(未認証public)","OAuth(CSRF非該当)") else "無/N-A"]+=1
print(f"{name}: 有効値={cnt['有']}")
print("列数:",len(hd)+len(newcols))
print("列数:", len(hd))
50 changes: 46 additions & 4 deletions tools/api-inventory/scripts/add_dataop4.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,52 @@
# -*- coding: utf-8 -*-
"""data_op_detail列: 取得/作成/更新/物理削除/論理削除 を実装から4区分評価"""
import ast,re,os
R="/home/mhaya/wekov2/"
import os as _os, sys as _sys
_sys.path.insert(0, _os.path.dirname(_os.path.abspath(__file__)))
from paths import data_path as _data_path

# 入出力: 既定は $WEKO_API_INVENTORY_DIR/weko3_api_list_full.tsv を in-place 更新。
# 以前は R+"weko3_api_list.tsv"(24列版)を読み書きしており、いま実行すると
# 台帳を壊す状態だった(当時これが作業用中間ファイルだったころの名残)。
TSV = _sys.argv[1] if len(_sys.argv) > 1 else _data_path("weko3_api_list_full.tsv")


def _write(path, hd, data, newcols):
"""既存の同名列は **その位置のまま値を差し替える**。無い列だけ末尾に足す。

末尾に付け直すと列順が変わり、README の awk 例や他スクリプトの
列位置前提(c[13]=impl_file 等)が壊れる。
"""
pos = {n: i for i, n in enumerate(hd)}
add_cols = [n for n in newcols if n not in pos]
out_hd = list(hd) + add_cols
force = _os.environ.get("WEKO_INVENTORY_OVERWRITE") == "1"
empty = ("", "-", "TODO")
lines = ["\t".join(out_hd)]
filled = 0
for c in data:
row = list(c) + [""] * (len(hd) - len(c))
vals = row[len(hd):] # このスクリプトが今回算出した値
body = row[:len(hd)]
for n, v in zip(newcols, vals):
if n not in pos:
continue
cur = body[pos[n]]
# 既存値は人手で精査されている。空欄/TODO のセルだけ埋める。
# 一括再生成すると判定が劣化する(bola_risk が逆転、data_op_detail の
# 論理/物理の区別が失われる、CSRF の指摘が消える等を実測で確認済み)。
if cur in empty or force:
if cur != v:
filled += 1
body[pos[n]] = v
extra = [v for n, v in zip(newcols, vals) if n not in pos]
lines.append("\t".join(str(x).replace("\t", " ") for x in body + extra))
open(path, "w", encoding="utf-8").write("\n".join(lines) + "\n")
print(f" → 空欄/TODO を埋めたセル: {filled}"
+ (" (WEKO_INVENTORY_OVERWRITE=1 のため既存値も上書き)" if force else ""))
R = _os.environ.get("WEKO_ROOT", "/home/mhaya/wekov2") + "/"
def load(p): return [l.rstrip("\n").split("\t") for l in open(p,encoding="utf-8") if l.rstrip("\n")]
rows=load(R+"weko3_api_list.tsv"); hd=rows[0]; data=rows[1:]
rows=load(TSV); hd=rows[0]; data=rows[1:]

# ファイル全体をキャッシュし、関数本体+同ファイル内で呼ぶヘルパも1段追う
filecache={}
Expand Down Expand Up @@ -75,6 +118,5 @@ def eval4(c,seg,method):
v=eval4(c,seg,method)
c.append(v)
if "論理削除" in v or "物理削除" in v: nc+=1
open(R+"weko3_api_list.tsv","w",encoding="utf-8").write("\t".join(hd+["data_op_detail"])+"\n"+
"\n".join("\t".join(str(x).replace("\t"," ") for x in c) for c in data)+"\n")
_write(TSV, hd, data, ['data_op_detail'])
print("data_op_detail付与。削除系(論理/物理):",nc,"列数:",len(hd)+1)
50 changes: 46 additions & 4 deletions tools/api-inventory/scripts/add_idempotency.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,50 @@
import ast,re,os
R="/home/mhaya/wekov2/"
import os as _os, sys as _sys
_sys.path.insert(0, _os.path.dirname(_os.path.abspath(__file__)))
from paths import data_path as _data_path

# 入出力: 既定は $WEKO_API_INVENTORY_DIR/weko3_api_list_full.tsv を in-place 更新。
# 以前は R+"weko3_api_list.tsv"(24列版)を読み書きしており、いま実行すると
# 台帳を壊す状態だった(当時これが作業用中間ファイルだったころの名残)。
TSV = _sys.argv[1] if len(_sys.argv) > 1 else _data_path("weko3_api_list_full.tsv")


def _write(path, hd, data, newcols):
"""既存の同名列は **その位置のまま値を差し替える**。無い列だけ末尾に足す。

末尾に付け直すと列順が変わり、README の awk 例や他スクリプトの
列位置前提(c[13]=impl_file 等)が壊れる。
"""
pos = {n: i for i, n in enumerate(hd)}
add_cols = [n for n in newcols if n not in pos]
out_hd = list(hd) + add_cols
force = _os.environ.get("WEKO_INVENTORY_OVERWRITE") == "1"
empty = ("", "-", "TODO")
lines = ["\t".join(out_hd)]
filled = 0
for c in data:
row = list(c) + [""] * (len(hd) - len(c))
vals = row[len(hd):] # このスクリプトが今回算出した値
body = row[:len(hd)]
for n, v in zip(newcols, vals):
if n not in pos:
continue
cur = body[pos[n]]
# 既存値は人手で精査されている。空欄/TODO のセルだけ埋める。
# 一括再生成すると判定が劣化する(bola_risk が逆転、data_op_detail の
# 論理/物理の区別が失われる、CSRF の指摘が消える等を実測で確認済み)。
if cur in empty or force:
if cur != v:
filled += 1
body[pos[n]] = v
extra = [v for n, v in zip(newcols, vals) if n not in pos]
lines.append("\t".join(str(x).replace("\t", " ") for x in body + extra))
open(path, "w", encoding="utf-8").write("\n".join(lines) + "\n")
print(f" → 空欄/TODO を埋めたセル: {filled}"
+ (" (WEKO_INVENTORY_OVERWRITE=1 のため既存値も上書き)" if force else ""))
R = _os.environ.get("WEKO_ROOT", "/home/mhaya/wekov2") + "/"
def load(p): return [l.rstrip("\n").split("\t") for l in open(p,encoding="utf-8") if l.rstrip("\n")]
rows=load(R+"weko3_api_list.tsv"); hd=rows[0]; data=rows[1:]
rows=load(TSV); hd=rows[0]; data=rows[1:]
srccache={}
def get_src(fp,ln):
key=(fp,ln)
Expand Down Expand Up @@ -34,6 +77,5 @@ def col_idem(c,seg):
seg=get_src(c[13],c[14]) if (len(c)>14) else ""
v=col_idem(c,seg); c.append(v)
if "★" in v: nc+=1
open(R+"weko3_api_list.tsv","w",encoding="utf-8").write("\t".join(hd+["idempotency"])+"\n"+
"\n".join("\t".join(str(x).replace("\t"," ") for x in c) for c in data)+"\n")
_write(TSV, hd, data, ['idempotency'])
print("idempotency ★状態遷移:",nc,"列数:",len(hd)+1)
Loading
Loading