AI

Anthropic Cyber Mission入門——OSS Scanner所見をFirestoreで受け止める

Anthropic Cyber MissionとOSS Scannerを題材に、モデル生成の脆弱性レポートをNext.js + Firestoreで受付・優先度付け・GitHub Issue化する設計を解説します。

2026年10月9日
AnthropicOSS ScannerセキュリティNext.jsFirestore
Anthropic Cyber Mission入門——OSS Scanner所見をFirestoreで受け止める

はじめに

AIで脆弱性を見つけられる時代になると、開発チームの課題は「発見できるか」だけではなくなります。

場面よくある困りごと
AIが脆弱性候補を大量に出す誤検知と本物の切り分けに時間がかかる
OSSや依存ライブラリの報告が届くどのプロダクトへ影響するか追跡しづらい
修正PRを急ぎたいGitHub Issue、担当者、検証結果が分散する

Anthropicは2026年10月8日に Anthropic Cyber Mission を発表しました。公式発表では、重要インフラ防御とオープンソースソフトウェアの保護を柱にし、その一部としてOSSプロジェクト向けの OSS Scanner を開始すると説明されています。

本記事では、架空の在庫管理SaaS「StockLane」を題材に、OSS Scannerのようなモデル生成レポートを受け取ったあと、Next.js App Router + Firestore + TypeScriptで「受付、トリアージ、GitHub Issue化、人間レビュー」へつなげる実装を作ります。この記事を読み終えると、AIセキュリティ所見をそのまま信じるのではなく、監査可能な作業キューとして扱えるようになります。

参考にした一次情報は次の通りです。

何が新しいのか

Anthropic Cyber Missionは、AIを使った防御側支援の長期的な取り組みです。発表では、重要インフラ向けのCritical Infrastructure Defense Programと、OSS向けのOSS Scannerが紹介されています。

OSS Scannerについて、Anthropicは「最強モデルでOSSコードの脆弱性を探し、OSSプロジェクトへ無償でセキュリティ監査を提供する」と説明しています。一方で、出力はモデル生成であり、人間によるレビューやトリアージなしで届く場合があるため、不正確または無効な報告が混ざり得るとも明記しています。

つまり、アプリ開発チーム側で大切なのは「AIが言ったから即修正」ではありません。AIの所見を、再現手順、影響範囲、証跡、担当、修正状況と一緒に残すことです。

ここではOSS Scannerの内部APIや専用CLIを仮定しません。公式発表で確認できるのは「OSS向けの脆弱性候補レポートを提供する取り組み」であり、アプリ側は受け取ったレポートを手動登録またはWebhook相当の入力として扱う設計にします。

事前準備

StockLaneでは、AIセキュリティ所見を securityFindings に保存します。脆弱性本文には機微な情報が含まれることがあるため、保存する項目は必要最小限にし、詳細なPoCや攻撃手順は社内の権限制御された場所へ分ける設計が安全です。

yarn add firebase-admin
GITHUB_OWNER=morison-example
GITHUB_REPO=stocklane
GITHUB_TOKEN=ghp_...
フィールド用途
sourceanthropic_oss_scanner、manual_review など
packageName影響を受ける依存ライブラリやモジュール名
severitylow、medium、high、critical
statusreceived、triaging、issue_created、fixed、false_positive
confidenceAI所見をチームがどれくらい信用しているか
githubIssueUrl作成したIssueへのリンク

ハンズオン1: 所見をFirestoreへ受け付ける

まず、外部から届いた脆弱性候補を保存するRoute Handlerを作ります。ここでは攻撃手順を長文のまま保存せず、要約、対象、推奨アクション、参照IDを中心に残します。

// src/app/api/security/findings/route.ts
import { getApps, initializeApp } from "firebase-admin/app";
import { FieldValue, getFirestore } from "firebase-admin/firestore";
import { NextRequest, NextResponse } from "next/server";

if (!getApps().length) {
  initializeApp();
}

const db = getFirestore();

type FindingBody = {
  source: "anthropic_oss_scanner" | "manual_review";
  externalId?: string;
  packageName: string;
  affectedPath?: string;
  title: string;
  summary: string;
  severity: "low" | "medium" | "high" | "critical";
  recommendation: string;
};

export async function POST(request: NextRequest) {
  const body = (await request.json()) as Partial<FindingBody>;

  if (
    !body.source ||
    !body.packageName ||
    !body.title ||
    !body.summary ||
    !body.severity ||
    !body.recommendation
  ) {
    return NextResponse.json({ error: "missing required fields" }, { status: 400 });
  }

  const findingRef = db.collection("securityFindings").doc();

  await findingRef.set({
    source: body.source,
    externalId: body.externalId ?? null,
    packageName: body.packageName,
    affectedPath: body.affectedPath ?? null,
    title: body.title,
    summary: body.summary,
    severity: body.severity,
    recommendation: body.recommendation,
    status: "received",
    confidence: "unreviewed",
    githubIssueUrl: null,
    createdAt: FieldValue.serverTimestamp(),
    updatedAt: FieldValue.serverTimestamp(),
  });

  return NextResponse.json({ findingId: findingRef.id }, { status: 201 });
}

この時点では「受け付けただけ」です。Anthropicの発表でも、モデル生成レポートには誤りがあり得るとされています。したがって、status: "received" から始め、人間の確認を通ってから開発キューへ流します。

ハンズオン2: 優先度を決める

次に、Firestore上の所見をレビューして優先度を決める関数を用意します。ここではAIに再判定させるのではなく、アプリ側のルールで最低限の交通整理をします。

type Severity = "low" | "medium" | "high" | "critical";

type FindingForTriage = {
  severity: Severity;
  packageName: string;
  affectedPath?: string | null;
  summary: string;
};

export function decideTriage(input: FindingForTriage) {
  const touchesAuth =
    input.affectedPath?.includes("/auth") ||
    input.summary.toLowerCase().includes("authentication") ||
    input.summary.toLowerCase().includes("authorization");

  if (input.severity === "critical") {
    return { priority: "P0", needsIssue: true, needsSecurityLead: true };
  }

  if (input.severity === "high" || touchesAuth) {
    return { priority: "P1", needsIssue: true, needsSecurityLead: true };
  }

  if (input.severity === "medium") {
    return { priority: "P2", needsIssue: true, needsSecurityLead: false };
  }

  return { priority: "P3", needsIssue: false, needsSecurityLead: false };
}

StockLaneでは、P0/P1は即Issue化、P2は通常スプリント、P3はまとめて確認というルールにします。AIの所見が多くなるほど、このような単純な判定ルールが効きます。人間は「全部読む」のではなく、「危ない順に検証する」状態へ移れます。

ハンズオン3: GitHub Issueを作る

最後に、レビュー済みの所見をGitHub Issueへ起票します。GitHubのREST APIでは、Issue作成は POST /repos/{owner}/{repo}/issues です。ここではSDKを追加せず、Node.jsの fetch で呼び出します。

type CreateSecurityIssueInput = {
  findingId: string;
  title: string;
  packageName: string;
  severity: "low" | "medium" | "high" | "critical";
  priority: "P0" | "P1" | "P2" | "P3";
  summary: string;
  recommendation: string;
};

export async function createGitHubIssue(input: CreateSecurityIssueInput) {
  const owner = process.env.GITHUB_OWNER;
  const repo = process.env.GITHUB_REPO;
  const token = process.env.GITHUB_TOKEN;

  if (!owner || !repo || !token) {
    throw new Error("GitHub credentials are not configured");
  }

  const response = await fetch(
    `https://api.github.com/repos/${owner}/${repo}/issues`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${token}`,
        Accept: "application/vnd.github+json",
        "X-GitHub-Api-Version": "2022-11-28",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        title: `[${input.priority}] ${input.title}`,
        labels: ["security", input.priority, input.severity],
        body:
          `Finding ID: ${input.findingId}\n\n` +
          `Package: ${input.packageName}\n` +
          `Severity: ${input.severity}\n\n` +
          `## Summary\n${input.summary}\n\n` +
          `## Recommended action\n${input.recommendation}\n\n` +
          "## Review checklist\n" +
          "- [ ] Reproduce or invalidate the finding\n" +
          "- [ ] Confirm affected versions and deployed services\n" +
          "- [ ] Add regression test or mitigation note\n" +
          "- [ ] Link the fixing PR",
      }),
    },
  );

  if (!response.ok) {
    throw new Error(`GitHub Issue creation failed: ${response.status}`);
  }

  return (await response.json()) as { html_url: string; number: number };
}

Issue本文には、攻撃を再現するための詳細を入れすぎない方が安全です。詳細なPoCや秘匿すべきログは、権限を絞った社内ドキュメントに置き、Issue側には検証チェックリストと参照IDだけを残します。

実務での運用ポイント

AI由来のセキュリティ所見は、スピードとノイズが同時に増えます。運用では次の4点を決めておくと破綻しにくいです。

ルール決めること
受付範囲OSS Scanner、手動報告、依存関係スキャンを同じ台帳に入れるか
誤検知処理false_positive にした理由と判断者を残す
SLAP0/P1/P2ごとの初動時間と修正期限
公開判断OSSへの報告、CVE、リリースノートの担当者

GitHub Actionsを使うなら、毎朝 securityFindings の received を集計し、P0/P1が残っているときだけSlack通知やIssue起票ジョブを走らせる構成が扱いやすいです。重要なのは、AIに「見つける」仕事を任せても、公開影響や修正優先度の責任はチーム側に残すことです。

まとめ

Anthropic Cyber MissionとOSS Scannerは、防御側がAIの力を使って脆弱性候補をより早く見つける流れを強める発表です。一方で、モデル生成の所見はそのまま真実として扱わず、誤検知、優先度、影響範囲、人間レビューを前提にしたワークフローが必要です。

Next.js + Firestoreで受付台帳を作り、重大度に応じてGitHub Issueへつなげるだけでも、AIセキュリティレポートは「読めない山」から「処理できるキュー」へ変わります。まずは1つのOSS依存や1つの社内モジュールを対象に、所見の受付、検証、修正PRまでを小さく回してみるのがおすすめです。