見守るのをやめた依存関係のための、早期警告システム。
AI の時代、あなたは維持できる以上のプロジェクトをリリースしています。Sentinello はそのすべてを見守り、Node.js 依存関係に潜む既知の CVE を浮かび上がらせます。忘れられたプロジェクトがインシデントになる前に。
コマンド1つで実行:
docker run -d \
--name sentinello \
-p 127.0.0.1:3870:3000 \
--stop-timeout 60 \
-v sentinello-data:/app/data \
-v sentinello-nvm:/home/sentinello/.nvm \
-v ~/Developer:/roots/personal:ro \
ghcr.io/walkofcode/sentinello:latestlinux/amd64 と arm64 で動作
アカウント不要。SaaSなし。テレメトリなし。Dockerイメージ1つとSQLiteファイル1つ — あなたのコードと検出結果がマシンの外に出ることはありません。
ポータルを使わずに済ませることもできます
同じスキャナーが CLI としても提供されています。インストール不要、アカウント不要、データベース不要 — 実行してエージェントが処理できるアドバイザリを出力し、そのまま終了します。
npx sentinelloパイプで渡すと stdout には markdown だけが流れるため、アドバイザリはそのままエージェントに届きます:
npx sentinello | claude -p "$(cat -)"フォルダーを走査
ディレクトリを指定すれば配下のすべてのプロジェクトを検出します。各プロジェクトのルートで停止するため、モノレポは 50 個ではなく 1 個として数えられます。.gitignore と .sentinelloignore は尊重されます。
3 つのソースを照合
lockfile から実際にインストールされている正確なバージョンを解決し、ローカルのアドバイザリキャッシュとオフラインで突き合わせます。プロジェクトごとのネットワーク通信はなく、コードは一切アップロードされません。
アドバイザリを書き出し
検出結果と修正プロンプトを添えた日付入りの markdown ファイル — 手を付ける前にトリアージし、override より親パッケージの更新を優先し、修正は lockfile で検証する、という指示付きです。
npm audit との違い
npm audit の置き換えではありません。npm audit を実行したうえで、npm audit からは見えない 2 つのアドバイザリソースを追加し、重複するものは抑制します。
| npm audit | npx sentinello | |
|---|---|---|
| 対象範囲 | 今いる 1 つのプロジェクトのみ。 | フォルダー配下のすべてのプロジェクトを、1 回の実行と 1 つのレポートで。 |
| アドバイザリソース | 利用中のレジストリのアドバイザリフィード。 | それに加えて OSV と GitLab gemnasium。重複は抑制されるため、ソースを増やしても新規の検出だけが加わります。 |
| 悪意あるパッケージ | 対象外。 | OSV の MAL- レコードがマルウェア入りで公開されたパッケージ(タイポスクワット、インストールスクリプトのペイロードなど)を、侵害された特定のバージョンと照合して警告します。 |
| 出力 | 表、または自分で解釈する JSON。 | 修正プロンプトを添えた markdown のアドバイザリ。そのままエージェントに渡せます。 |
| 照合方法 | プロジェクトごとにレジストリへ問い合わせ。 | ローカルキャッシュに対してオフラインで照合。初回実行時に確認のうえダウンロードし、以降はほとんど通信しません。 |
追加のソースは形だけのものではありません。Sentinello 自身のリポジトリでは、npm audit と OSV はそれぞれ 3 件を報告し、その内容はすべて一致します。一方、唯一の critical な検出は、他の 2 つが持たない gemnasium から得られています。
機能
セルフホスト型ポータルにすべてを — 外部サービスなし、データがネットワークの外に出ることもありません。
単一のトリアージキュー
十数個のチェックアウトに散らばった npm audit ではなく、ポートフォリオ全体のCVEを1か所で確認・トリアージできます。
プロジェクト別・ライブラリ別に見る
任意のリポジトリを開いて、検出結果、修正バージョン、履歴を掘り下げる — あるいは脆弱なパッケージに切り替えて、影響を受けるすべてのプロジェクトを確認し、一度にすべてをミュートできます。
一致した検出結果
複数のソースが同じ脆弱性を報告しても、検出結果は 1 件のままです。どのソースが一致したかを併記し、そのいずれかが付けた最も重い評価で扱います。
継続的なスキャン
バックグラウンドのワーカーがスケジュールに沿って再スキャンするので、確認を覚えていなくても新しいアドバイザリが表示されます。
複数のソース
npm audit を超えて、OSV と GitLab gemnasium の両方と照合し、より広い CVE カバレッジと既知の悪意あるパッケージの検出を実現します。
通知とWebhook
失敗や検出のアラートを、Slack、Telegram、またはシンプルなWebhookで受け取れます — ルートまたはプロジェクト単位でスコープ指定でき、言語も選べます。自動修正エージェント向けに JSON またはプレーンテキストのペイロードを送信します。
MCP サーバー
Claude Desktop や Cursor などの MCP クライアントを接続し、チャットを離れずに検出結果・プロジェクト・ライブラリを照会し、スキャンを実行できます。
アドバイザリのエクスポート
プロジェクトまたはライブラリの検出結果を Markdown でエクスポート。チームや LLM 向けにカスタマイズ可能な修復プロンプト付き。
1つのイメージ、1つのファイル
Dockerイメージ1つとSQLiteファイル1つ。データベースサーバーも、メッセージキューも、クラウドへの依存もありません。
自動登録されるRoots
/roots にマウントしたものは起動時に登録・スキャンされます — ディレクトリ名がそのラベルになります。
プロジェクトごとのNode
各プロジェクトの .nvmrc を尊重し、固定されたNodeのバージョンを一度だけインストール・キャッシュします。
10言語
ポータルのUI、スキャンの理由コード、ステータスは10言語にローカライズされています。
スクリーンショット
実際の動作を見る — いくつかのデモプロジェクトをスキャンするポータル。任意の画像をクリックすると拡大表示します。
比較
Sentinello は重い Dependency-Track でも安価な Snyk でもありません。別のニッチを担います。誰もパイプラインに組み込まなかったプロジェクトのロングテールです。
| Sentinello | Dependency-Track | Snyk | Dependabot | |
|---|---|---|---|---|
| 設定不要 — フォルダーを指定するだけ | ~ | ~ | ||
| SBOM / CI ステップ不要 | ||||
| 実際の解決済みロックファイルをスキャン | ~ | |||
| 悪意あるパッケージの検出 | ||||
| セルフホスト、SaaS なし | ||||
| 単一イメージ + SQLite | ||||
| AI ネイティブ(MCP + エクスポート) | ~ | |||
| 多言語(Python、Go、…) | ||||
| エンタープライズポリシー / VEX | ~ | ~ |
Dependency-Track は、誰かが SBOM パイプラインで計装したプロジェクトしか見えません。Sentinello は忘れられたものを見つけます。エンタープライズポリシーでは相手が優れています — 成熟したパイプラインで既に使っているなら、そのまま使ってください。Sentinello は、誰も見ていない残りのポートフォリオのためのものです。
私たちがこれを作った理由
AIの時代、あなたは維持できる以上のものをリリースしています。
今や1人の開発者が、年に十数件のプロジェクトを立ち上げ、納品し、そして離れていきます — マーケティングサイト、顧客向けダッシュボード、ひっそりと本番に出た個人プロジェクト。これらを安全に保つには、かつてはチェックアウトごとに SSH で入って手作業で npm audit を回すか、Next.js のCVEを公開から数日後にニュースの見出しで知るしかありませんでした。十数個ものリポジトリでそんなことを続けられる人はいないので、結局まったく行われません。
クリティカルなリモートコード実行の欠陥を抱えた、忘れられた依存関係が1つあれば十分です。見るのをやめた最も単純なサイトが、侵入口になります。
「Snyk や Dependabot を使えばいいのでは?」 — それらは、あなたが組み込んだ CI パイプラインの中で動きます。そしてロングテールにはそのパイプラインがありません。Sentinello は、それ以外すべてのための早期警告システムです。フォルダーを指定すれば、忘れていたすべてのプロジェクトを見守り、新しいCVEがインシデントになる前に1つのキューに浮かび上がらせます。
仕組み
3ステップ。プロジェクトにインストールするエージェントも、作成するアカウントも不要です。
コードを指定する
リポジトリを /roots にマウントするか、設定 → Roots から追加します。各ディレクトリは起動時に自動登録・検出されます。
継続的にスキャン
バックグラウンドのワーカーがスケジュールに沿って依存関係を既知のCVEと照合し、各プロジェクトが固定するNodeのバージョンを必要に応じてインストールします。
1つのキューでトリアージ
すべてのプロジェクトのすべての検出結果が、深刻度で絞り込める1つのキューに集まります。Slack、Telegram、Webhookへの通知も任意で設定できます。
対象となる人
Sentinelloは、見守れる以上のものを本番に抱えるすべての人のためのものです — 1人で開発する人、小さなチーム、顧客の仕事をやりくりするエージェンシー。
- リリース後もずっと安全であり続ける必要のある、個人プロジェクトや顧客サイトをリリースしている。
- すべてのリポジトリにCIを組み込まずに、ポートフォリオ全体を俯瞰したい。
- コードの一覧をSaaSに渡すより、セルフホストしたい。
成熟したパイプラインにSnykやDependabotをすでに組み込んでいる大規模な組織なら、それを使い続けてください — Sentinelloはエンタープライズ向けSCAを置き換えようとはしていません。誰も見ていない、残りのポートフォリオのためにあります。オープンソースかつMITライセンスなので、何をしているかを正確に読むことができます。
リリースノート
Sentinello は頻繁に更新されています。各リリースの内容をご紹介します。
1 つのソースがプロジェクト全体を代弁していたダッシュボード
v3.5.0 · 2026/08/20- ダッシュボードの<strong>状態</strong>列は 1 つのソースだけを報告し、残りを捨てていました。スキャンはソースごとに 1 行を書き込み、それらは数ミリ秒差で終わるため、列には最後に終わったもの — 実際には常に OSV — が表示されていました。npm audit が問題なくスキャンして実際の脆弱性を見つけていたのに、どのプロジェクトも「OSV データベースは未ダウンロード」と表示されていたのです。状態は応答できなかったソースごとにバッジを 1 つずつ持ち、それぞれソース名を示すようになり、有効なソースがすべて正常なら何も表示しません。スキャン履歴にも同じ理由で<strong>ソース</strong>列が加わりました。1 回の実行で見分けのつかない 3 行が書き込まれていたためです。
- 設定 → ソースは、キャッシュの再構築中に「最新」だと主張していました。ポータルが読む状態は同期の完了時にしか書かれなかったため、数分かかる再構築のあいだずっと以前の件数を表示し続けていました — その間、各スキャンは半分削除されたキャッシュを正しく拒否していたのに。さらにスキャナーが要求する正規化バージョンを一度も持っていなかったため、バージョンの更新だけで再構築なしに同じ虚偽の主張が生じていました。行は現在、以前の件数を薄く表示した<strong>再構築中…</strong>、またはキャッシュが使用不能で誰も対処していないときは<strong>再構築待ち</strong>と表示します。
- ダウンロードが完了したキャッシュがあっても、各プロジェクトはキャッシュが無かったときの判定のまま固定されていました。再スキャンが行われないため、<code>osv_db_not_seeded</code> は次の定期実行まで、あるいは気づいてスキャンを押すまで残り続けました。Sentinello はキャッシュが再び使用可能になった時点で、フルスキャンを 1 回キューに入れるようになりました。増分更新では何もキューに入れません。 アップグレードも対象です。キャッシュの完了前に付いた判定をプロジェクトが抱えたままの環境にこのリリースが入ると、ワーカーは最初の起動でその食い違いに気づき、自動的に解消します。スキャンを忘れずに実行する必要はありません。
修正済みバージョンを指摘していたアドバイザリ
v3.4.0 · 2026/08/18- gemnasium の一部のアドバイザリは、比較演算子とバージョンの間に空白を入れて書かれています — <code><0.5.2</code> ではなく <code>< 0.5.2</code> です。パーサーはこの組を 2 つの別々のトークンとして読んでいました。取り残された <code><</code> は空の境界を受け取り、単独で残ったバージョンは厳密な固定値としてキャッシュされます。その結果、<code>fresh</code> のアドバイザリはバージョン 0.5.2 を脆弱と報告していました。0.5.2 こそがこの問題を修正したリリースであるにもかかわらずです。しかも修正版なしとして報告されます。固定されたバージョンはアップグレード先をまったく持たないからです。この書き方のアドバイザリは 19 件あり、そのすべてが誤って解析されていました。15 件は範囲を完全に失い、7 件はレコード自身が修正版として挙げているバージョンを固定していました。<code>pg</code> では 11 件です。
- Sentinello は npm の範囲構文を自分で解釈するのをやめ、まず npm 自身の実装に範囲を渡すようになりました。これにより、一連の誤読がまとめて解消されます。npm にとって <code><=3.3</code> は「3.3 系列の最後まで」を意味します。文字どおりに読むと 3.3.0 で止まり、3.3.1 を見落としていました。これは実在する <code>converse.js</code> のアドバイザリです。また <code>=103</code> は単一の点ではなく 103 系列全体を意味し、<code>binaryen</code> は 8 件でこの書き方をしています。キャレット、チルダ、<code>1.x</code>、ハイフンの範囲も読めるようになりました。以前はいずれかで書かれたレコードは何も言わずに捨てられていました。gemnasium が npm 向けに公開する 4,696 件すべての個別バージョン範囲で確認したところ、自前で解釈すると npm と 9 件food食い違い、委譲すると 1 件も食い違いません。影響を受けるのは npm だけです。Python の <code>==1.0</code> はワイルドカードではなく厳密なバージョンなので、そこに npm の規則を当てはめると検出を捏造してしまいます。
- npm の読み方はパッケージをインストールするうえでは正しく、アドバイザリとしては誤っている点が 3 つあり、それらは適用を見送っています。バージョンをまったく挙げない範囲 — <code>*</code>、<code>x</code>、空欄 — は、npm がインストールするときは「任意のバージョン」を意味しますが、アドバイザリでは「誰も記入しなかった」ことと区別がつきません。そのため、公開された全リリースに対する検出に変えるのではなく拒否します。末尾に紛れ込んだ <code>||</code> が、その手前の範囲をすべてに広げることもなくなりました。さらに <code>>0</code> は全バージョンを意味したままです。<code>pandora-doomsday</code> は悪意あるパッケージをこの書き方で宣言しており、npm の読み方では 0.x 系がすべて無害と判定されてしまうからです。別件として、宣言された修正版が自身の影響範囲の開始位置と同じかそれより下にあるアドバイザリでは、その値を境界として差し込まなくなりました。何にも一致しない区間、つまり黙って報告しなくなる検出を生んでいたためです。そして、そうした区間を捨てる規則はソースごとに書くのではなく OSV と共有するようになりました。個別に書いていたことこそが、両者の食い違いの発端でした。
- これらすべてを見逃し続けてきたテストは、入力を列挙するのではなく生成するようになりました。gemnasium が npm 向けに公開している個別のバージョン範囲すべてを、ビルドのたびに npm 自身の実装と突き合わせます。あわせて範囲文法の全組み合わせと、OSV の範囲イベントのあらゆる順序も検証します。前リリースに対して実行すると、この一括検証は 28 件の範囲で失敗します。これが置き換えた手書きの 10 例はすべて通っていました。今後、上流が新しい書き方を生み出しても、誰かがその結果生じた検出に気づいた時点ではなく、最初に取り込まれた時点でビルドが失敗します。
- これらの検出結果を支えるコードのすべてがテストで実行されるようになりました。ステートメント、分岐、関数、行のいずれも100%で、リポジトリ全体に除外はひとつもありません。これは直近数リリースの不具合がなぜ表に出たのかへの直接の答えです。いずれも何も実行されない分岐で、何もしないまま成功を報告していました。最後の109件を塞ぐ過程で、到達不能な安全確認として記録されていたものが実際には単に未テストだった例がいくつも見つかり、さらに、参照先のファイルが移動して以来何も検査していなかったカバレッジ規則も判明しました。その規則と周囲の500行の例外はひとつの規則に置き換わり、テストのないコードは平均値を下げるのではなくビルドを失敗させます。
- 最後の防御上の欠落も解消しました。読み取れない OSV の上限は有効な区間を開いたままにし、フォールバック境界は適用後に検証され、<code><1.2.3-0</code> のような明示的な npm プレリリース境界は正確な意味を維持します。
すべてのバージョンを脆弱だと言い張るアドバイザリ
v3.3.2 · 2026/08/17- 影響範囲に終端のないアドバイザリは、あらゆるバージョンに永久に一致します。どれだけアップグレードしても消えない検出結果であり、画面上では本当に未修正の脆弱性と見分けがつきません。<code>xlsx</code> の 2 件がまさにその状態で届き、完全に修正済みの 0.20.3 を「高」深刻度・修正なしとして 11 プロジェクトで同時に報告していました。一方 <code>npm audit</code> は同じ 2 件を 0.19.3 および 0.20.2 で修正済みと正しく報告していました。原因は上流にあり、しかも意図的なものです。GitHub は、そのパッケージ名でレジストリが配信していない修正バージョンを記載しません。SheetJS は 0.19.3 以降を自社 CDN からのみ公開しているため、GitHub は範囲を「0 以降すべて」と記し、本当の境界は別のフィールドに記録します。Sentinello はそのフィールドを読んでいませんでした。今は読みます。影響を受けていた npm アドバイザリは 15 件で、<code>babel-traverse</code> や <code>sandbox</code> も含まれます。本当に未修正の 480 件はこれまでどおりそう表示され、すでに自身の境界を示しているレコードが上書きされることはありません
- 修正バージョンを列挙しながら範囲を開いたままにしていた gemnasium のアドバイザリは、そのうち最も高いバージョンで区切られるようになりました。終端のない範囲は将来のリリースまで脆弱だと主張することになり、修正を挙げているレコードがそれを意味することはあり得ません
- gemnasium は Python のバージョン範囲を PEP 440 の積集合 — <code>>=5.0,<5.8</code> — として記述しますが、パーサはトークンを空白でしか分割していなかったため全体が 1 つのトークンになり、読み取れない下限と、上限なしという結果になっていました。この状態の範囲は何にも一致せず、何にも一致しないということは何も報告しないということです。キャッシュ済み PyPI レコード 7,159 件のうち 2,830 件が該当していました。これは 3.3.0 で Python・Go・Rust を取り下げた 3 つの理由のひとつでした。残る 2 つは未解決のため、これらは取り下げられたままです
3.3.0 と同じリリース、ただしビルドできる Docker イメージ付き
v3.3.1 · 2026/08/15- 3.3.0 は npm と GitHub には公開されましたが、コンテナイメージのビルドに失敗したため、リリース時点では GHCR にも Docker Hub にも 3.3.0 がありませんでした。Dockerfile はインストールするワークスペースパッケージを 1 つずつ列挙しますが、そのうちバージョン比較のパッケージだけが最初から記載されていませんでした。今回のリリースまでイメージ内の何もそれを import していなかったため、この欠落は一度も表面化しませんでした。その後、3.3.0 自身のアプリケーションコードから修正済みの 3.3.0 イメージを公開したので、<code>docker pull sentinello:3.3.0</code> は再び動作します。3.3.1 は同じ修正をソースツリーに取り込んだものです。CLI を使っている場合は 3.3.0 のままで問題ありません。
本当に動くエコシステムだけを — そして信頼できる通知
v3.3.0 · 2026/08/15- 通知が空のまま届くことがありました。あるオペレーターは、プロジェクトに脆弱性があると告げながら一件も列挙していない Telegram メッセージを受け取りました。配信はプロジェクト単位で絞り込まれる一方、実行はスキャナーごとだったため、npm audit のパスは OSV の未配信イベントを 2 件渡され、どれとも一致しないまま見出しだけを空のリストの上に描画していました。さらに両イベントを配信済みとして記録したため、一度も言及されなかった 2 件の検出結果が通知済みとして扱われ、配信済みイベントは二度と見直されません。今は説明できるイベントだけを配信し、説明できないものは保留のまま次回スキャンで再検討されます
- 検出結果は、いずれかのソースが付けた最も重い評価で報告されます。このエスカレーションはダッシュボード、プロジェクト合計、CLI の <code>--fail-on</code> ゲートには届いていましたが、通知のしきい値には届いていませんでした。通知が裏付け処理より前に走るため、イベントには生き残ったソース自身の評価が刻まれ、その後書き換えられることがなかったのです。実際のインスタンスでは、135 件の未解決の検出結果が本来より低いイベント重大度を持ち、<strong>うち 41 件は critical を low・high・moderate として記録</strong>していました。critical と high に絞った通知先は、そのどれについても永久に呼び出されなかったことになります
- スケジュールされたスキャンが日をまたがずに深夜で止まっていました。「07:00 から 3 時間ごと」は 07、10、13、16、19、22 時に実行され、その後は翌日の 07:00 まで動かず — 1 日 8 回のはずが 6 回、毎晩 9 時間の空白ができていました。その間も設定画面は選んだ間隔をそのまま表示し続けます。「20:00 から 6 時間ごと」に至っては 1 日 1 回でした。時刻枠は日付をまたいで続くようになりました
- <strong>Python、Go、Rust を取り下げました。</strong> 粗さへの慎重さではありません。これらの不具合は「問題なし」として報告されていました。修正版の導出とバージョン順序が semver 専用のため、OSV の Django アドバイザリはインストール済みの 4.2 に対して「3.2.23 へ更新」を勧めていました。OSV の PyPI パッケージ名は PEP 503 で正規化されておらず、リゾルバ側の名前と決して一致しません。gemnasium の範囲パーサーは PEP 440 のカンマ交差を読めません。誤った理由で「脆弱性なし」と答えるソースは、そもそも提供しないソースより有害です。したがって製品面から完全に取り除きました — スイッチも、検出も、ダウンロードもありません。<strong>すでに収集済みの検出結果は引き続き表示・ミュートでき、何も削除されません。</strong> npm エコシステムの表示名は <strong>Node.js</strong> になりました。言語ではなく、実際にスキャンしているパッケージエコシステムを指す名前です
- <strong>設定 → ソース</strong> をこれに合わせて作り直しました。以前は各ソースが自分のスイッチの隣で同じ説明を繰り返し、キャッシュを持つソースはそれぞれ 5 行のステータスパネルとフルサイズの「今すぐ更新」ボタンを抱えていました — そのボタンは何度現れてもソースごとに 1 つの共有シグナルを積むだけなのにです。スイッチはソースが有効かどうかだけを示すようになり、各ソースが何を追加し、何をどこからダウンロードし、いつ動くのかは下の 1 つの参照表に移りました。同期状態は 1 行に収まっています
- npm 自身のロックファイルが本番から到達可能だと述べているパッケージが、開発専用に降格されていました。ロックファイルに <code>dev: true</code> がないことは、そのパッケージが本番から到達可能だという npm の主張であり、ルートのマニフェストより強い言明です。ところがリゾルバは、名前が <code>devDependencies</code> にも現れるたびにそれを上書きしていました — まさに npm が正しい場面です。実際の 130 プロジェクトで計測したところ、97 プロジェクトで 142 個のパッケージが降格されていました(lodash、semver、postcss、tailwindcss、@babel/runtime)。あるインスタンスでは未解決の検出結果 7 件が本番のみのフィルタから隠れていました
- <code>0.0.0-20180523222229-09b5706aa936</code> のようなバージョンに固定されたパッケージは、<strong>どのアドバイザリにも一致しません</strong>でした。上限のないものにさえ一致せず、スキャンは検出 0 件で ok と報告していました。アドバイザリの下限 <code>introduced: 0</code> がリリース 0.0.0 として比較され、semver ではプレリリースがリリースより下に並ぶためです。実際のキャッシュにある比較可能な 19,085 個の範囲のうち 330 個が、何も満たせない区間として保存されていました
- 中断された OSV 同期がアドバイザリを永久に消すことがありました。増分パスはアドバイザリの行を削除してから置き換えを try/catch の中で取得していたため、タイムアウト・5xx・シャットダウンのいずれでも丸ごと消えてしまい、そのうえカーソルは無条件に進んで ID を永久に後方へ置き去りにしていました。損失は無言で、次のフル再シードまで残り、しかも成功と報告された同期の中で起きます。<code>npx sentinello</code> のキャッシュも不安定な実行のたびに同じように削られていました。どちらも先に取得し、後で置き換えるようになりました
- CLI の <code>--dep-type dev</code> は「dev から到達可能であればよい」を意味していましたが、ポータルは「dev <em>だけ</em>から到達可能」を意味します。そのため両方から到達できるパッケージは一方のビューに現れ、もう一方には現れませんでした — あるインスタンスでは 2 つの解釈で 177 件の未解決の検出結果が食い違います。CLI はポータルの規則を使うようになり、OSV の <code>withdrawn</code> フィールドも尊重します。以前は構造上それを読めませんでした。実際の npm キャッシュでは 585 行がこの値を持ち、そのすべてが有効な検出結果として報告されていました
- 脆弱性がある版<em>より後</em>から始まると述べる gemnasium のアドバイザリ、つまり <code>>=1.2.8</code> ではなく <code>>1.2.8</code> が、境界の版そのものも影響を受けるかのように読まれていました。2021 年の <code>rc</code> パッケージ乗っ取りはまさにこの書き方で、1.2.8 は最後のクリーンなリリース、つまりアドバイザリ自身の対処方法が「留まれ」と指示している版です。<code>rc</code> を導入しているすべてのプロジェクトに、修正版なしの重大なマルウェア検出が、一度も侵害されていない版に対して表示されていました。境界はアドバイザリの記述どおりに保持されるようになり、この形の誤検出は消えます
- 同じ丸めは逆方向にも働き、本物の検出を隠していました。<code><=2.0.0</code> で区切られたアドバイザリは「2.0.0 未満」として保存されていたため、最も明示的に指し示されている 2.0.0 が報告されず、影響を受ける版をちょうど一つだけ挙げるアドバイザリは空の範囲になって丸ごと破棄されていました。以前から存在していたのに見えていなかった検出がいくつか新たに現れます
- Sentinello が実装していない構文で書かれた範囲、たとえば <code>^1.0.0</code> や <code>~1.0.0</code> は、その文字列そのものを厳密な版として保存していたため、キャッシュに残っている限り何にも一致できませんでした。有効に見えて決して発火しないアドバイザリです。こうしたレコードは、機能しない形で保存する代わりに拒否するようになりました。また、上限に明確な修正版を持たないアドバイザリにも、ようやくアップグレードの提案が出ます
- Webhook URL やボットトークンをログ行に届く前にマスクするヘルパーが、短いものをそのまま出力していました。6 文字以下の値は拒否する一方で、先頭 8 文字と末尾 4 文字を残しており、その 2 つが重ならないことを誰も確認していなかったのです — つまり 7〜12 文字の秘密情報はすべて丸ごと返っていました。今は少なくとも 8 文字を隠すか、値を完全に伏せます
検出結果にどのソースが一致したかを表示し、撤回済みアドバイザリの報告を停止しました
v3.2.0 · 2026/08/15- 複数のアドバイザリデータベースが同じ脆弱性を報告する場合、Sentinello は常に検出結果を 1 件にまとめてきました。3 つのデータベースが知っているからといって同じ欠陥を 3 回報告するのはノイズだからです。しかし従来は統合されたソースの情報をすべて破棄していたため、npm audit・OSV・GitLab gemnasium が独立に確認した脆弱性が、1 つのデータベースしか報告していないものとまったく同じに見えていました。実際のインスタンスではこれが全検出結果の 3 分の 2 にあたります。今後は各検出結果がそれを報告した他のソースを保持し、残った 1 件のバッジと並べて表示します
- 検出結果は、いずれかのソースが付けた最も高い深刻度で報告されます。データベース間の評価は実際に食い違います(gemnasium は CVSS ベクトルから算出し、npm audit は GitHub の区分を採用します)。スキャナーにとっては慎重な読み方こそ行動の基準です。これは見た目だけの変更ではありません。引き上げられた検出結果は、ダッシュボード、プロジェクト合計、CLI の <code>--fail-on</code> ゲート、通知しきい値のいずれでも区分が変わります。アップグレード後の最初のスキャンで件数が動くことがありますが、新たに検出されたものはなく、同じ検出結果をより慎重に評価しているだけです
- ソース間で評価が分かれている場合、検出結果の深刻度の横に、各ソースが実際に何と述べたか(そのソース独自のアドバイザリ ID と評価)を開くコントロールが表示されます。説明すべき不一致があるときだけ表示されるため、全ソースの評価が一致する検出結果は簡潔なままです
- OSV は撤回を専用のフィールドに記録し、GitHub は撤回されたアドバイザリを <code>npm audit</code> に届く前に取り除きますが、GitLab gemnasium のスキーマにはそのようなフィールドがありません。gemnasium はレコードを書き換えることで撤回します。タイトルが「False Positive」「Withdrawn Advisory: …」「Duplicate Advisory: …」に変わる一方で、それまで挙げていたバージョンはそのまま残ります。Sentinello はそのバージョンを読み取り、GitLab が明確に取り下げた検出結果を報告していました。JavaScript・Python・Go・Rust を通じて 383 件のレコードが該当し、その中には「False Positive」というタイトルで <code>express</code> を報告するものもありました。これらはすべて破棄されます。383 件のうち 278 件は他のアドバイザリと重複するため撤回されたものなので、重複した検出結果の一群も同時に解消されます
- この判定は撤回マーカーを完全一致で照合し、語句を本文中から探すことはしません。そのため、偽陽性そのものを扱う正当なアドバイザリは引き続き報告されます。「Cosign’s verify-blob-attestation reports false positive when payload parsing fails」という題の Cosign の CVE-2026-39395 は影響を受けません
- gemnasium のキャッシュはこのアップグレード後の最初の同期で自動的に再構築され、その時点で撤回済みアドバイザリは消えます。操作は不要で、毎日の同期で実行されます。「設定 → ソース → 更新」からすぐに実行することもできます
アドバイザリのバージョン範囲が両方向で正しくなりました
v3.1.1 · 2026/08/14- GitLab gemnasium のアドバイザリの中には、機械可読なバージョン範囲をまったく持たないものがあります(JavaScript では 10,777 件中 698 件)。Sentinello はその空白を「列挙された最初の修正版より前のすべてが影響を受ける」と仮定して埋めていましたが、この一覧は順不同でリリースブランチごとに 1 つの修正版を含むため、その推測はしばしば誤ったブランチを指していました。protobufjs 7.6.5 は当該ブランチが 7.5.5 で修正済みであるにもかかわらず重大なリモートコード実行として報告され、3 件の別々のアドバイザリがそれぞれ 8.0.5 未満のすべての vite を脆弱だと主張していました。Sentinello はもう範囲を捏造しません。別の識別子で公開された同一のアドバイザリ、アドバイザリ自身の説明文、または OSV を有効にしている場合に限りお使いのマシン上にある OSV のコピーから本来の範囲を復元し、いずれでも判明しない場合は推測せずにそのレコードを破棄します。いくつかの重大な検出結果が消えるはずです
- OSV は複数のリリースブランチで修正されたアドバイザリを、ブランチごとに個別のエントリとして記述します。Sentinello は最初の 1 件だけを残して他を破棄していました。JavaScript だけで 1,927 件の脆弱なバージョン範囲が失われ、そのすべてが見えなくなった実際の脆弱性です。minimatch のアドバイザリは 8 つのブランチを対象としますが 1 つしか残らず、インストール済みの minimatch 3.0.4 や 9.0.0 は報告されませんでした。next や ua-parser-js も同様にブランチを失っていました。現在はすべてのブランチが保持されます。新しい検出結果が現れるはずですが、それらの脆弱性は以前から存在しており、単に見えていなかっただけです
- このアップグレード後の最初の同期で、2 つのアドバイザリキャッシュはいずれも自動的に再構築されます。保持されている範囲が以前のコードで生成されたものだからです。操作は不要で、毎日の同期で実行されます。待ちたくない場合は「設定 → ソース → 更新」からすぐに実行できます
ミュートした検出結果は視界から外れ、データベースは無限には増えなくなりました
v3.1.0 · 2026/08/13- ミュートした検出結果は、すでにあなたが下した判断です。そのためグレー表示で残るのではなく、プロジェクトページから完全に外れるようになりました。見た目が整うだけではありません。見出しの件数、両方のタブのバッジ、ページ送り、ライブラリごとの合計、エクスポートボタン — ページ上のあらゆる数値が同じ行から算出されるため、ダッシュボード、MCP ツール、アドバイザリのエクスポート(いずれも以前からミュート分を除外していました)とページの数字がようやく一致します。「ミュート済みを表示」を切り替えればいつでも戻せます。検出結果が*すべて*ミュートされているプロジェクトでも同様です
- ダイアログでの入力が、1文字でフォーカスを失うことはなくなりました。この不具合により、ミュートダイアログの「理由」欄 — 入力必須で、数か月後にミュートの妥当性を確認できる唯一の手がかり — が事実上入力不能になっていました。同じダイアログが、開いた元のテーブル行の配置を引き継ぐこともなくなりました。検出結果のミュートでは右寄せのダイアログになるのに、プロジェクトのミュートではならなかったのはこれが原因です
- スキャン履歴が際限なく増えることはなくなりました。これまで古さを理由にスキャン行が削除されたことは一度もなく、ディスク上に残るプロジェクトはスイープごとにソース単位で行を無制限に積み上げていました — 実際の環境では3か月足らずで 2.2 GB に達しました。「設定 → 詳細設定」に保持期間(既定は90日)が追加され、ワーカーが毎時それより古いものを削除します。各プロジェクトの最新100件は常に保持されます。検出結果、ミュート、通知履歴が削除されることはありません。対象はスキャンログだけです。90日という既定値なら、アップデートした環境が初回実行で何かを削除することはありません。履歴が実際にその期間を超えたとき、またはご自身で短くしたときに初めて削除が始まります
- その増加の大半は `npm audit` の生出力でした。成功したスキャンごとにそのまま保存されながら、どこからも読まれていません — その環境のデータベースの 98.7% がこれでした。現在は短い要約だけを記録するため、1行あたり約 79 KB から 100 バイト程度になります。上限も設けたので、どのスキャナーも同じことを繰り返せません
- MCP では、ミュートしたプロジェクトが `get_dashboard_summary` の集計から外れる一方で `list_projects` は引き続き返すことを明記しました。両者は意図的に異なる母集団を数えており、それを比較したエージェントが不具合と受け取っていました。`list_scans` も各スキャンのスキャナー生出力を返さなくなりました。1回の応答で 16 MB 前後に達することがあったためです
gemnasium のダウンロードが復旧し、CLI がターミナルを返すようになりました
v3.0.1 · 2026/08/04- 3.0.0 では GitLab gemnasium のダウンロードが全ユーザーで `HTTP 406` により失敗していました。Node 組み込みの fetch はプログラムから削除できない `Sec-Fetch-Mode: cors` ヘッダーを付与し、GitLab はそれを含むリポジトリアーカイブ要求をすべて拒否します。つまりネットワークや IP、再試行回数とは無関係でした。ダウンロードは通常の HTTPS 要求を使うようになり、成功します
- アーカイブはブランチ名ではなくコミット ID で取得するようになりました。同じ上流コミットから更新する全員がキャッシュされた 1 つのコピーを共有するため、各自が GitLab に 60 MB のアーカイブを生成させる必要がありません。7 分近くかかっていた初回ダウンロードが数秒で完了します
- CLI は処理をすべて終えても — レポートを書き、サマリーを表示しても — ターミナルを返しませんでした。ダウンロードの接続が背後で開いたままプロセスを生かしていたためです。アーカイブを読み終えた時点で閉じるようになりました
- ダウンロードを拒否されたソースが、報告するまで 3 分間止まることはなくなりました。数秒で報告し、ターミナルでは再試行を提案します。再試行するのは実際に失敗したソースだけです
- `--fail-on` は双方向で正直になりました。アドバイザリソースを参照できなかった実行は、実施していないクリーンなスキャンとして報告せず拒否します。また `SENTINELLO_OSV_FEED_URL=off` や `SENTINELLO_GEMNASIUM_FEED_URL=off` で自分が無効にし一度もダウンロードしていないソースを理由に失敗することはなくなりました
Sentinello はポータルなしでも動くようになりました
v3.0.0 · 2026/08/03- スキャナーが npm 上の CLI として提供されます。`npx sentinello` はフォルダーを走査して配下のすべてのプロジェクトを見つけ、npm audit・OSV・GitLab gemnasium と照合し、修正プロンプトを添えた markdown のアドバイザリを書き出します。インストール不要、アカウント不要、データベース不要で、コードがマシンの外に出ることもありません
- パイプで渡すと stdout にはアドバイザリだけが流れるため、`npx sentinello | claude -p "$(cat -)"` は文書を壊すことなく完全な作業リストをエージェントに渡せます
- 初回実行でダウンロードを拒否されて gemnasium ソースを失うことがなくなりました。GitLab はアーカイブを 1〜2 分ほどまとめて拒否しますが、以前の再試行は 13 秒で諸めていました。CLI は待機して待ち、待っている理由を表示し、既定の 3 分が合わない場合は `--feed-wait` を受け付けます
- ダウンロード見積もりはどちらも推測ではなく実測しました。OSV の npm エクスポートは 196 MB ではなく 204 MB、gemnasium のアーカイブは 80 MB ではなく 52 MB と表示します。確認プロンプトは見積もりにチルダを付け、サーバーが報告したサイズと取り違えられないようにしています
- オプションのように見える値は、そのまま解釈せず拒否するようになりました。`--out --` は以前、プロジェクト内に `--` という名前のファイルへアドバイザリを書き出し、成功と報告していました
- リリースの内容が多いときに「新着情報」パネルがウィンドウ下部からはみ出さなくなりました
アドバイザリ文書が実際に届くように — 集計も正確に
v2.6.0 · 2026/07/29- get_project_advisory が文書そのものを返すようになりました。これまで接続クライアントにはファイル名と件数などのメタデータだけが渡され、完全な作業リストと説明されていたにもかかわらず文書本体は届いていませんでした
- アドバイザリの書き出しが、スキャナーの行ごとではなくアドバイザリごとに 1 件へ統合されました。npm audit と OSV の両方が報告する脆弱性は、両方の ID を持つ 1 件の作業項目になります。ポータルの「.md をダウンロード」にも同じ変更が適用され、件数がダッシュボードと一致するようになりました
- MCP の 1 回の応答に収まらない大きなプロジェクトはページ分割されます。文書が未完であることを明示し、残りを取得するための正確な呼び出しを示すため、エージェントが残りを「問題なし」と誤読することがありません
- すべての MCP ツールのすべての入力に説明が付きました。また、unmute に必要なミュート ID を取得できる list_mutes ツールを追加しました(従来は同じセッションでミュートを作成した場合しか分かりませんでした)
- 深刻度の集計の抜けを修正しました。既知の 5 段階以外の深刻度を持つ検出は、件数には数えられるのにどの区分にも入らず、その 1 件だけを持つプロジェクトが完全にクリーンに見えていました
アドバイザリの書き出しを MCP から直接
v2.5.0 · 2026/07/28- 接続済みの MCP クライアントは、新しい get_project_advisory ツールでプロジェクトの Markdown アドバイザリ全文を取得できます。ポータルの「.md をダウンロード」と同じ文書を、ブラウザからコピーせずに使えます
- ミュートした検出結果はプロジェクトのアドバイザリ書き出しに含まれなくなりました。すでにリスクを受け入れた項目がエージェントに渡ることはありません
- 注意: アドバイザリには書き出しプロンプトが含まれるため、MCP クライアントは設定 → 書き出し に記入した内容を読み取れるようになりました
切れないポップアップと、より厳格な書き出しプロンプト
v2.4.3 · 2026/07/26- ドロップダウン、依存パスのポップオーバー、アドバイザリ書き出しメニューが、置かれているテーブルやダイアログに切り取られなくなりました。ページの上に重ねて描画され、下に余裕がない場合は上向きに開きます
- アドバイザリ書き出しの既定プロンプトが、何かを編集する前に計画を立てること、同じ修正でまとめて解消できる検出結果をグループ化すること、バージョン変更ごとのコードへの影響を明示することを求めるようになりました。目標はゼロ件ですが、ミュート・バージョン範囲の緩和・スキャン範囲の縮小といった見せかけのゼロへの近道は禁止し、本当に解消できないものは日付入りの残存項目テーブルに残します
ブランチを独立した列に
v2.4.2 · 2026/07/25- プロジェクトをスキャンした git ブランチが、プロジェクト名の下ではなくプロジェクト一覧の独立した列に表示されるようになりました(アイコンなしのテキスト)
クリーンな終了
v2.4.1 · 2026/07/25- コンテナを再起動しても、書き込み途中のスキャンが強制終了されなくなりました。ワーカーは約 30 秒間リトライすることなく、すぐに起動します
- compose ファイルに stop_grace_period: 60s(または --stop-timeout 60)を設定して余裕を持たせてください。README と Docker ドキュメントに説明を追加しました
ポリグロット解析 — npm に Python・Go・Rust が加わりました
v2.4.0 · 2026/07/25- Sentinello が npm に加えて Python・Go・Rust のプロジェクトも解析するようになりました。ロックファイルは完全にオフラインで解決され、各プロジェクトが解析カバレッジ(完全・部分的・監査不可)を報告するため、抜け漏れが見えないまま残りません
- GitLab の gemnasium データベースが npm audit と OSV に並ぶオフラインのアドバイザリソースとして加わり、CVE/GHSA エイリアスで他ソースと重複排除されます。「設定 → ソース」は「言語 × ソース」のマトリクスになり、セルごとに通知範囲を設定でき、ソースが 1 つ以上有効であれば npm audit 自体も無効にできます
- 検出結果に、それが得られた git ブランチが記録され、プロジェクト一覧・プロジェクトヘッダー・すべての通知に表示されます
- プロジェクトの各行に操作が用意されました。今すぐスキャン、アドバイザリのコピーまたはダウンロード、ミュートと解除、タグ編集ができ、トリアージのたびに各プロジェクトを開く必要がなくなりました
- プロジェクトダッシュボードが約 3.3 秒から約 0.03 秒になり、画面遷移では固まったように見える代わりにローディング表示が出るようになりました
- セキュリティ: 依存関係のアドバイザリ 25 件を解消しました。ポータルの画像最適化で実際に有効だった libvips の CVE や、配布されるポータルに影響する Next.js の 9 件を含みます
- アドバイザリ書き出しの既定プロンプトが、最小公開経過日数・ロックファイルの検証・古い override を扱うようになりました
よりシンプルな MCP 設定 — 環境変数は不要
v2.3.0 · 2026/06/09- MCP の設定はすべて「設定 → MCP」で完結します。トークンを生成すると /api/mcp エンドポイントがオンになり、削除するとオフになります — SENTINELLO_MCP_ENABLED と SENTINELLO_MCP_API_TOKEN の環境変数は廃止されました(既存の環境変数トークンはアップグレード時に一度だけ取り込まれます)
- Claude Code、Codex、Cursor、Claude Desktop 向けの貼り付けるだけの接続スニペット。トークンが入力済みです
- SENTINELLO_PORTAL_BASE_URL を環境変数で設定している場合、優先され起動のたびに再適用されるため、「設定 → 詳細設定」では読み取り専用で表示されます
誤検知の低減と、自動で整理される検出結果
v2.2.0 · 2026/06/09- マルウェアのアドバイザリが、影響を受ける正確なバージョンと照合されるようになりました。かつて侵害されたパッケージでも、クリーンな、または修正済みのバージョンはもう検出されません
- 重複した検出結果が次回のスキャンで自動的に解決され、古い項目や取り残された項目が自動でクリアされます
- 本番(production)と開発(development)のラベルが、すべてのソース(npm と OSV)で一貫した単一の方法で算出されるようになりました
すっきりしたプロジェクトヘッダーと一貫したフィルター
v2.1.0 · 2026/06/06- プロジェクトヘッダーを簡素化 — タイトルの横で名前を変更でき、ミュートとタグはアイコンに
- 依存タイプフィルターの横の新しいドロップダウンから、ソース(npm / OSV)で検出結果を絞り込み
- アプリ全体でドロップダウンを統一し、タイムゾーンなどの長いリストでは入力して検索可能に
よりわかりやすいアップグレード手順
v2.0.1 · 2026/06/04- 2.0 の破壊的変更に関するアップグレード手順を拡充
- README に localhost のみのポートバインドを明記
複数ソースのスキャンと、デフォルトで安全な堅牢化されたインストール
v2.0.0 · 2026/06/04- 任意の第2ソースとしての OSV(設定 → ソース、デフォルトはオフ)。悪意あるパッケージ検出を備え、ローカルキャッシュ内の公開 OSV データベースと照合します
- 検出結果がソース間で統合されるようになりました。脆弱性ごとに1行で、各ソースをタグ付けし、利用可能な最良の修正と依存パスの和集合を示し、ソースフィルターと依存パスのポップオーバーを備えます
- セキュリティ強化: MCP エンドポイントはデフォルトでオフかつトークンが必要、Webhook 配信は SSRF から保護、任意のポータルログインゲート、コンテナは非特権ユーザーとして実行されます
- 設定が、サイドバーとプロフィールページを備えたトップレベルのセクションになりました
MCP 連携と新着情報
v1.4.0 · 2026/05/29- Claude Desktop、Cursor などのクライアント向けの /api/mcp の MCP サーバー
- サーバー URL とトークン管理を備えた新しい「設定 → MCP」セクション
- 新着情報バッジとリリースノートの履歴
フッターのバージョン表示の修正
v1.3.1 · 2026/05/28- 実行中のバージョンがフッターに正しく表示されます
通知の改善
v1.3.0 · 2026/05/28- 環境ごとに通知をフィルタリング
- 通知先の編集フォームを簡素化
- 既存の通知先を複製
プロジェクトとライブラリのページ
v1.2.0 · 2026/05/24- ホーム画面が専用のプロジェクトページとライブラリページに分割されました
スケジュールのライブ再読み込み
v1.1.2 · 2026/05/24- ポータルで変更を保存すると、ワーカーがスキャンスケジュールをすぐに再読み込みします
より安全な削除と分かりやすい更新バナー
v1.1.0 · 2026/05/23- ルートと通知先を削除する前に確認
- 更新のお知らせが画面上部の閉じられるバナーに変更
- ホストのマウントが消えると、ワーカーが古いルートを整理します
スキャナーの精度修正
v1.0.1 · 2026/05/23- インストール済みバージョンが実際には脆弱な範囲にない検出結果を除外
- 送信履歴のある通知先を削除できるように
初のオープンソースリリース
v1.0.0 · 2026/05/23- Sentinello の最初の一般公開リリース
ロードマップ
Sentinello は今、複数の言語にまたがってあなたの依存関係を見守っています。これからの方向性と、あなたがリクエストできることを紹介します。
よりスマートな優先順位付け
予定悪用可能性と脆弱なコードが実際に到達可能かで検出結果をランク付けし、重要なものから対応します。
より多くの連携
予定通知チャンネルを増やし、チームがすでに使っているツールと Sentinello をつなぐ方法を広げます。
静的解析(SAST)
予定依存関係の既知の CVE だけでなく、あなた自身のソースコードの危険なパターンも検出します。
シークレットとライセンスのスキャン
予定コミットされたシークレットやライセンスの問題を、同じポートフォリオの同じキューで検出します。
次に連携・スキャンしてほしいものを教えてください — GitHub で Issue を立てて、ロードマップづくりに参加しましょう。