取り組みの背景
AIコーディングエージェントは強力ですが、その力ゆえに危険を内包しています。無制限に実行させると:
- 無限ループ により CPU を占有
- 大量削除 により重要なファイルが失われる
- データ漏洩 により API キーが流出
- プロンプトインジェクション により指示を改ざんされる
ここではAntigravity におけるエージェント安全設計の全体像を解説し、本番環境での実装パターンを示します。
エージェント暴走の5つのリスク
1. 無限ループ / リソース枯渇
// ❌ 危険な例
while (true) {
const result = await generateCode();
if (!result) break; // 条件が満たされず永遠に実行
}
// ✅ 安全な例
const MAX_ITERATIONS = 5;
let iterations = 0;
while (iterations < MAX_ITERATIONS) {
const result = await generateCode();
iterations++;
if (result.success) break;
}対策:
- 最大反復回数の設定
- CPU/メモリ使用率の監視
- タイムアウト時間の厳密化
2. 誤削除 / ファイルシステム破壊
// ❌ 危険な例(プロンプトインジェクションで「全削除」を指示されるケース)
async function deleteArtifacts(agentInstruction) {
const path = agentInstruction.parse(); // 攻撃者の指示を受け入れる
await fs.rm(path, { recursive: true, force: true });
}
// ✅ 安全な例
const ALLOWED_DIRS = [
'/tmp/build',
'/tmp/artifacts',
'/tmp/cache'
];
async function deleteArtifacts(agentInstruction) {
const path = agentInstruction.parse();
if (!ALLOWED_DIRS.some(d => path.startsWith(d))) {
throw new Error(`Deletion blocked: ${path} not in allowlist`);
}
await fs.rm(path, { recursive: true });
}対策:
- ファイル削除を必要最小限に制限
- Allowlist / Denylist による path フィルタリング
- Dry-run モードで確認後に実行
3. 認証情報 / API キーの流出
// ❌ 危険な例
const output = `API_KEY=${process.env.OPENAI_API_KEY}\n${result}`;
console.log(output); // ログに API キーが出力される
// ✅ 安全な例
const sanitized = output.replace(/sk_[a-z0-9]+/g, 'sk_***');
const sanitized2 = sanitized.replace(/AIzaSy[a-zA-Z0-9_-]+/g, 'AIzaSy***');
console.log(sanitized2);対策:
- 環境変数を Code Artifact に含めない
- 出力を regex で自動サニタイズ
- Secret scanning ツール(GitHub、GitLab 標準機能)を有効化
4. プロンプトインジェクション攻撃
// ❌ 危険な例
const userInput = getUserInput(); // 悪意あるプロンプト
const systemPrompt = `You are a helpful coding agent. ${userInput}`;
const response = await gemini.generate(systemPrompt);
// ✅ 安全な例(構造化入力)
const systemPrompt = `You are a helpful coding agent.
Follow the rules below exactly. Do not deviate.`;
const userInstruction = {
task: "add a button",
scope: "Button.tsx only", // 構造化フィールド
allowedActions: ["modify", "add"] // 厳密に制限
};
const response = await gemini.generate({
system: systemPrompt,
user: JSON.stringify(userInstruction),
template: "structured"
});対策:
- ユーザー入力を構造化データ(JSON)として受け取る
- Free-form テキストは避ける
- Prompt template を固定し、プレースホルダーのみ可変にする
5. ネットワーク漏洩 / 不正な外部通信
// ❌ 危険な例
const response = await fetch(userProvidedUrl); // どこへでも通信可能
// ✅ 安全な例(Allowlist)
const ALLOWED_HOSTS = [
'api.github.com',
'api.stripe.com',
'cdn.example.com'
];
async function safeFetch(url) {
const parsed = new URL(url);
if (!ALLOWED_HOSTS.includes(parsed.hostname)) {
throw new Error(`Network access denied: ${parsed.hostname}`);
}
return fetch(url);
}対策:
- 外部 API ホスト名の Allowlist 化
- DNS over HTTPS (DoH) で通信を暗号化
- Egress パケットの Default-Deny ポリシー
多層防御アーキテクチャ
本番環境では、単層の防御では不十分です。複数層を組み合わせた多層防御(Defense in Depth) を実装します:
┌─────────────────────────────────────────────────┐
│ Layer 1: Input Validation & Sanitization │
│ (プロンプト検査・構造化フォーム) │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ Layer 2: Execution Sandbox (Firecracker/gVisor) │
│ (リソース隔離・OS分離) │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ Layer 3: Resource Limits (CPU/Memory/Disk) │
│ (ハードウェア制約の強制) │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ Layer 4: Network Policy (Zero-Trust Egress) │
│ (ネットワーク通信の制御) │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ Layer 5: Output Validation (Secret Scanning) │
│ (出力検査・認証情報検出) │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ Layer 6: Human Review (Artifacts) │
│ (人間による最終検証) │
└─────────────────────────────────────────────────┘
実装パターン
パターン 1: Rules による受動的ガードレール
// Antigravity の Rules で定義
const agentRules = {
rules: [
{
id: "no_delete_production",
condition: "action === 'delete'",
action: "block",
message: "Deletion of production files is forbidden"
},
{
id: "max_iterations",
condition: "iterations > 5",
action: "stop",
message: "Max iteration reached. Review artifacts and proceed manually."
},
{
id: "require_review",
condition: "impact_level === 'high'",
action: "pause",
message: "High-impact changes require manual review"
}
]
};利点:
- 宣言的で可読性高い
- コード変更なしに Rules を更新可能
- エージェントに前もって制約を知らせる
欠点:
- ルール数が増えると複雑化
- 想定外の状況に対応できない
パターン 2: Workflows による能動的監視
// Workflow で Agent の実行を監視・制御
const agentWorkflow = {
stages: [
{
stage: "generation",
agent: "writer",
timeout: "30s",
onFail: "revert"
},
{
stage: "validation",
agent: "critic",
checks: [
"no_syntax_errors",
"no_hardcoded_credentials",
"performance_acceptable"
],
onFail: "reject_and_report"
},
{
stage: "testing",
agent: "tester",
environment: "sandbox", // Firecracker で隔離実行
timeout: "60s",
onFail: "escalate_to_human"
},
{
stage: "approval",
agent: "human",
type: "artifacts_review",
required: true
}
]
};利点:
- エージェント間の協調を明示的に制御
- 各段階で Pass / Fail を判定
- 失敗時の自動フォールバック
パターン 3: Skills による機能の細粒度制御
// カスタム Skill で許可された操作のみ実装
class FilesystemSkill {
async readFile(path) {
if (!this.isAllowed('read', path)) throw new Error("Access denied");
return fs.readFile(path, 'utf-8');
}
async writeFile(path, content) {
if (!this.isAllowed('write', path)) throw new Error("Access denied");
// Dry-run で内容をチェック
await this.validateWriteContent(content);
return fs.writeFile(path, content);
}
async deleteFile(path) {
if (!this.isAllowed('delete', path)) throw new Error("Access denied");
// 削除前に Allowlist で確認
if (!this.SAFE_DELETE_DIRS.some(d => path.startsWith(d))) {
throw new Error(`Deletion blocked: ${path}`);
}
return fs.rm(path);
}
isAllowed(action, path) {
const policy = this.getPolicy(action);
return policy.allowlist.some(pattern => path.match(pattern));
}
}利点:
- 機能単位でアクセス制御を実装
- 細粒度の権限分離
- エージェントが使える API を限定
サンドボックス技術の選択
| 技術 | セキュリティ | パフォーマンス | 複雑度 | 用途 |
|---|---|---|---|---|
| Firecracker | 最高(カーネル隔離) | 高速(VM) | 高い | 本番運用・多テナント環境 |
| gVisor | 高(システムコール制御) | 中(sandboxed kernel) | 中 | SaaS・ホスト環境 |
| Docker | 中(簡単に脱出) | 高速(コンテナ) | 低 | 開発・テスト環境のみ |
| Process isolation | 低(カーネル共有) | 最高速 | 最低 | 検証・デモ |
推奨:
- 本番環境 → Firecracker
- スケーラブル SaaS → gVisor + cgroup
- チーム開発環境 → Docker(内部 VM 上)
リソース制限の設定例
// Antigravity での実行設定
{
execution: {
resource_limits: {
cpu: "2 cores", // 最大 2 CPU
memory: "4 GB", // 最大 4 GB RAM
disk: "20 GB", // 最大 20 GB storage
network_bandwidth: "100 Mbps" // 最大帯域
},
timeout: {
generation: "30 seconds",
testing: "60 seconds",
total: "300 seconds" // 合計 5 分上限
},
monitoring: {
cpu_threshold: "80%", // 80% 超過時に警告
memory_threshold: "90%",
rate_limit: "100 API calls / minute"
}
}
}フィールド解説:
cpu— max CPU cores。多くの場合 2–4 で十分memory— max heap memory。Node.js では default 2 GBdisk— temp dir のクォータnetwork_bandwidth— egress データレートtimeout— 各ステージごとの制限時間monitoring— 超過時のアラート閾値
マルチエージェント検証チェーン
複数エージェントを直列化し、各段階で品質をチェック:
const multiAgentValidation = {
agents: [
{
stage: 1,
role: "Writer",
task: "Generate code",
output: "artifacts"
},
{
stage: 2,
role: "Critic",
task: "Review for security, performance, maintainability",
checks: [
"no_hardcoded_secrets",
"memory_efficient",
"follows_style_guide"
]
},
{
stage: 3,
role: "Tester",
task: "Run tests in isolated sandbox",
environment: "firecracker_vm",
coverage_threshold: "80%"
},
{
stage: 4,
role: "Validator",
task: "Final security scan",
checks: [
"dependency_vulnerability",
"sast_scan",
"secret_scan"
]
}
]
};このチェーンにより、各エージェントが独立した観点から検査し、見落としを大幅に削減できます。
まとめ
Antigravity のエージェントを安全に運用するには:
- 入力検証 — プロンプトを構造化、ユーザー入力は制限
- 実行隔離 — Firecracker / gVisor で OS 分離
- リソース制限 — CPU / Memory / Disk / Network に上限
- 多層防御 — Rules + Workflows + Skills + Human Review
- 監視・スキャン — Secret scanning + vulnerability scan + performance monitoring
これらを組み合わせることで、エージェント暴走のリスクを最小限に抑えながら、Antigravity の力を最大限に活用できます。
さらに詳しく学ぶには以下を参照してください:
- Antigravity エージェントメモリパターン — エージェント状態管理のベストプラクティス
- Antigravity マルチエージェントプロダクションパターン — 本番運用の実装例
- Antigravity TDD テスト自動化ガイド — テスト駆動開発での安全性向上
🎁 関連書籍
エージェントセキュリティの深い理解には以下が参考になります: