Skip to content

fix: severity の変換を eslint.config.js から出して、テストで守る - #71

Merged
isamu merged 2 commits into
mainfrom
fix/config-severity-transform
Aug 27, 2026
Merged

fix: severity の変換を eslint.config.js から出して、テストで守る#71
isamu merged 2 commits into
mainfrom
fix/config-severity-transform

Conversation

@isamu

@isamu isamu commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #48issue は測り直して書き換えたうえで閉じます —— 当初の記述(型検査されていない 3 ファイル)は、測ったら実態と違っていました。

元の #48 が言っていたことは、もう問題ではありません

3 ファイルのうち 2 つは #56 / #59 / #62 で解決済みです。残る eslint.config.js について、現実的な設定ミス 7 種を実際に仕込んで何が捕まえるか測りました:

仕込んだ誤り yarn lint yarn lint:overrides
rulesrule の綴り違い
severity warnwarning
ルール名の綴り違い 素通り
filesfile の綴り違い
オプションの型違い(max: "600"
オプションのキー綴り違い
実在しないトップレベルキー

7 種すべて既存のゲートが捕まえます。ルール名の綴り違いだけ yarn lint が素通りさせ、#67 で入った lint:overrides が捕まえます。型検査を足すには eslint-plugin-securitydeclare module シムが要り、それは実質 any なので安全を足しません。

測っている最中に、本当の穴が出ました

eslint.config.js はロジックを持っています。 raise 変換がプリセットの warnerror に上げます —— 「CI で warn は無価値」だから。そしてそれを守るものが何もありませんでした。

raise の中身を entry => entry にすると:

yarn lint            exit 0
yarn lint:overrides  exit 0
yarn test            exit 0

全部緑のまま、実際には 14 件が error → warn に落ちます。全部 security プラグインです:

detect-child-process            detect-eval-with-expression
detect-non-literal-fs-filename  detect-object-injection
detect-unsafe-regex             detect-possible-timing-attacks
detect-non-literal-require      detect-pseudoRandomBytes        … 他 6 件

警告は CI を落とさず、lint の出力を読むものはありません。security の指摘 14 件が黙って参考情報に格下げされても誰も気づかない状態でした。型検査では捕まりません —— raise は型としては正しいまま、意味だけが変わります。

Items to Confirm / Review

  1. eslint.severity.js.js であること。 eslint.config.js が直接 import し、ESLint はあのファイルをそのまま読むので、.ts にすると 1 つの関数のために jiti をローダに入れることになります。型は eslint.severity.d.ts で与えています(@ts-expect-error では黙らせていません)。
  2. eslint.config.d.tsdefaultunknown[] と宣言していること。 構造的な型を書くのは、このリポジトリが制御できない形を主張することになります。読む側が絞る方針です(scripts/overrides-report.ts が実際にそうしていて、読めないブロックは報告します)。
  3. 決め手のテストがフィクスチャではなく実 config を見ていること。 フィクスチャは壊れた変換とも辻褄が合ってしまうので、「解決後の config のプリセットブロックに warn が 1 つも残っていない」を直接主張しています。per-file の DEBT ブロックは意図的に warn なので除外しています。

挙動保存の証明

旧 config と新 config の解決結果を全キー比較 —— ルールの severity と各ブロックの全キー、497 キーで差分 0

break-verify

4 変異すべて撃墜:

変異 結果
entry => entry(元の穴そのもの) killed
"warn" だけ見て数値 1 を見落とす killed
オプションを捨てて "error" を返す killed
off まで上げてしまう killed

ゲート

終了コードで確認: format:check 0 / lint 0 / typecheck 0 / test 0(537 pass)/ typecheck:summary 0(床 3 つとも維持)/ lint:overrides 0。

User Prompt

  • issue は閉じられるか
  • 1 本でまとめて直す

Summary by Sourcery

Extract and test the ESLint severity policy so preset warnings cannot silently bypass CI enforcement.

Bug Fixes:

  • Protect the ESLint preset severity policy by ensuring warning-level preset rules are promoted to errors while disabled rules remain unchanged.
  • Prevent silent weakening of security and other preset rules by validating the resolved ESLint configuration directly.

Enhancements:

  • Extract ESLint severity transformation logic into a separately typed JavaScript module for direct testing.
  • Preserve rule options and non-rule configuration while applying severity changes across preset blocks.
  • Add an explicit ambient type declaration for the untyped ESLint configuration export.

Tests:

  • Add unit and mutation-resistant tests covering warning promotion, numeric severities, disabled rules, option preservation, immutability, and configuration-level enforcement.

Summary by CodeRabbit

  • New Features

    • ESLint preset rules configured as warnings are now treated as errors, helping prevent important findings from being overlooked.
    • Existing rule options and configuration settings are preserved.
  • Bug Fixes

    • Improved consistency when applying rule severity across the linting configuration.
  • Tests

    • Added coverage confirming severity promotion, option preservation, and complete configuration enforcement.

Closes #48

eslint.config.js はロジックを持っている。raise 変換がプリセットの warn を error に
上げる —— 「CI で warn は無価値」だから。そしてそれを守るものが何も無かった。

raise の中身を entry => entry にすると lint / lint:overrides / test すべて exit 0 の
まま、実際には 14 件が error -> warn に落ちる。全部 security プラグインで、
detect-child-process / detect-eval-with-expression / detect-non-literal-fs-filename
など。警告は CI を落とさず lint の出力を読むものも無いので、security の指摘 14 件が
黙って参考情報に格下げされても誰も気づかない。

これは型検査では捕まらない。raise は型としては正しいまま、意味だけが変わる。

raise / enforced を eslint.severity.js に出した。eslint.config.js が直接 import する
ので TypeScript にはできない(ESLint はあのファイルをそのまま読む。.ts にすると 1 つの
関数のために jiti をローダに入れることになる)。型は eslint.severity.d.ts で与えている
—— @ts-expect-error で黙らせない。

決め手のテストはフィクスチャではなく解決後の実 config に対して「プリセットのブロックに
warn が 1 つも残っていない」と主張する。フィクスチャは壊れた変換とも辻褄が合うため。
eslint.config.d.ts はその import のために置いたもので、default を unknown[] と宣言して
いる —— 制御できない形を構造的に主張しないため。読む側が絞る。

挙動保存: 旧 config と新 config の解決結果を全キー比較(497 キー、ルールの severity と
ブロックの全キー)—— 差分 0。

break-verify: 4 変異とも撃墜(恒等関数 / "warn" だけ見て数値 1 を見落とす /
オプションを捨てて "error" を返す / off まで上げてしまう)。

#48 は当初「型検査されていない 3 ファイル」だったが、2 つは #56/#59/#62 で解決済みで、
残る 1 つについては現実的な設定ミス 7 種を仕込んで測ったところ全部が既存のゲートに
捕まった(うちルール名の綴り違いは yarn lint が素通りさせ、#67 の lint:overrides だけが
捕まえる)。型検査を足すには eslint-plugin-security の declare module シムが要り、それは
実質 any なので安全を足さない。issue を実態に合わせて書き換えたうえで閉じる。

全ゲートを終了コードで確認: format:check 0 / lint 0 / typecheck 0 / test 0 (537 pass) /
typecheck:summary 0(床 3 つとも維持)/ lint:overrides 0。

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

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

Sorry @isamu, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 3 days by commenting @sourcery-ai review. Upgrade to get a review now.

@sourcery-ai

sourcery-ai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Reviewer's Guide

ESLint の severity 昇格処理を eslint.config.js からテスト可能な plain-JS モジュールへ分離し、型宣言を添えて、warn の文字列・数値形式を error に変換しつつオプションや off を保持する契約をユニットテストと実 config の検証で保護しています。

Flow diagram for ESLint rule severity conversion

flowchart TD
    Entry[Rule entry] --> Array{Array with options?}
    Array -->|yes| ReadArray[Read first severity]
    Array -->|no| ReadValue[Read entry severity]
    ReadArray --> Warning{warn or numeric 1?}
    ReadValue --> Warning
    Warning -->|yes| Raise[Return error and preserve options]
    Warning -->|no| Keep[Return entry unchanged]
    Raise --> Result[Converted rule entry]
    Keep --> Result
Loading

File-Level Changes

Change Details Files
抽出した severity 変換ロジックに型情報を付与し、プリセットの警告ルールだけをエラーへ昇格する挙動を明示した。
  • raise が文字列・数値の warnerror に変換する
  • ルールオプションを保持し、erroroff などは変更しない
  • enforced がルール以外の設定を保持し、入力を変更せずに変換する
  • 実行環境上の理由からロジックを plain JavaScript とし、対応する declaration files を追加する
eslint.config.js
eslint.severity.js
eslint.severity.d.ts
eslint.config.d.ts
severity ポリシーを独立したユニットとして検証し、実際に解決された ESLint config でもプリセットの警告残存を検出できるようにした。
  • 文字列・数値 severity、オプション保持、off 維持、非 rules ブロック、非破壊性をテストする
  • 実 config を import して、per-file の DEBT ブロックを除くプリセットに warn が残っていないことを検証する
  • 無効化された変換や severity の取りこぼし、オプション破棄、off の誤昇格を検出できるテスト構成にする
test/test_eslintSeverity.ts

Assessment against linked issues

Issue Objective Addressed Explanation
#48 Move the severity-raising logic (raise / enforced) out of eslint.config.js into a separately testable module, while preserving its behavior of converting preset warn/numeric 1 severities to error, preserving options, and leaving non-warning severities unchanged.
#48 Add tests that protect the real configuration behavior, particularly ensuring that resolved preset configuration contains no remaining warn severities while intentionally excluding per-file debt blocks.

Possibly linked issues


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

@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

Next included review available in 48 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6e4bac44-8057-4e7c-8c9e-11dfba5f42dc

📥 Commits

Reviewing files that changed from the base of the PR and between 50c984e and 171844b.

📒 Files selected for processing (1)
  • eslint.config.js

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3ce27509-f949-4710-8610-e5eb6ca13bb6

📥 Commits

Reviewing files that changed from the base of the PR and between 2e88549 and 50c984e.

📒 Files selected for processing (5)
  • eslint.config.d.ts
  • eslint.config.js
  • eslint.severity.d.ts
  • eslint.severity.js
  • test/test_eslintSeverity.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The PR extracts ESLint severity enforcement into a separate JavaScript module, adds declarations for the module and config, and adds unit and integration tests for warn-to-error conversion.

Changes

ESLint severity enforcement

Layer / File(s) Summary
Severity transformation module
eslint.severity.js, eslint.severity.d.ts
Adds raise and enforced helpers. Warn-level severities become errors while options, disabled rules, and other config properties remain unchanged.
ESLint configuration integration
eslint.config.js, eslint.config.d.ts
Imports enforced from the new module and declares the config export as unknown[].
Severity transformation validation
test/test_eslintSeverity.ts
Tests severity conversion, option preservation, immutability, unchanged blocks, and the resolved ESLint config.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 50c98

This localized ESLint configuration change preserves the intended severity behavior and adds tests around it; no actionable merge-blocking risk remains after normal checks and review.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly states that severity conversion is moved out of eslint.config.js and protected by tests. It matches the main change.
Linked Issues check ✅ Passed The pull request satisfies issue #48. It extracts raise and enforced into eslint.severity.js and adds tests against the resolved ESLint config to detect warn-level regressions.
Out of Scope Changes check ✅ Passed All changes support issue #48. The implementation, declarations, and tests are directly related to extracting and protecting the severity conversion logic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 5…
Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 5 files.

✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/config-severity-transform

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.

@isamu

isamu commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

CI が赤かった件 — このブランチのせいではありませんでした

check (22.x)check (24.x) が落ちていましたが、原因はこの PR の外です:

src/publishChecks.ts:2243       File has too many lines (1303). Maximum allowed is 1301
test/test_publishChecks.ts:2144 File has too many lines (1533). Maximum allowed is 1500

このブランチはその 2 ファイルを触っていません(差分は eslint.config.js / eslint.severity.js / 2 つの .d.ts / 新しいテストのみ)。

起きていたこと:

  1. feat: 記事に署名の欄を持たせる(byline, 0.32.0) #66(byline)がマージされて publishChecks.tstest_publishChecks.ts が伸びた
  2. chore: warn を昇格できるものは昇格し、台帳の主張を検査される形にする #68 で入れた max-lines のラチェットに引っかかり、main が赤くなった
  3. fix: byline のぶんラチェットを上げる(main が赤い) #72 が天井を上げて直した
  4. この PR の CI は 2 と 3 のあいだの main との合成で走っていた

手元でその合成を再現したところ、yarn lint は exit 0(9 warning / 0 error)でした。最新の main(60ac1df)を取り込んで push し直しています。

副次的な確認になりますが、#68 のラチェットは意図どおり働いています。 ファイルが天井を越えて伸びたことを実際に検出し、直すまで main を赤にしていました。ラチェットが「実行不能なノイズになって最初にぶつかった人に消される」のではないか、というのは #67 のレビューで出た懸念でしたが、ここでは越えた本人が同じ日に天井を上げて片付いています。

main 取り込み後、全ゲートを終了コードで確認: format:check 0 / lint 0 / typecheck 0 / test 0(541 pass)/ typecheck:summary 0(床 3 つとも維持)/ lint:overrides 0。raise を恒等関数にする変異も、取り込み後に再実行して赤くなることを確認済みです。

@isamu
isamu merged commit 54768b4 into main Aug 27, 2026
8 checks passed
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.

eslint.config.js がロジックを持っていて、それを守るものが何も無い

1 participant