risk-assessment-summarization について
ServiceNow GRC のリスク管理チームやレポート作成者向けのリスク評価要約スキルです。スコア、固有リスクと残余リスク、傾向、イベントデータを集約し、ヒートマップ用データと軽減策を作成します。
主な機能
- 01リスクスコアと分類の集約
- 02固有リスクと残余リスクの比較
- 03可能性・影響ヒートマップデータの生成
- 04リスク傾向の時系列分析
- 05リスクエクスポージャーに基づく軽減策の提案
- 06リスクイベントと統制有効性の関連付け
活用シーン
- 01リスク委員会や取締役会向け報告書の作成
- 02事業部門間のリスク状況の比較
- 03リスク軽減投資の効果評価
MCP 対応
SN-Query-Table でリスク、定義、イベントを取得し、SN-Execute-Background-Script で集計分析を実行します。SN-Natural-Language-Search と SN-Discover-Table-Schema も利用対象として記載されています。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ happy-technologies-llc/ happy-platform-skills --skill risk-assessment-summarizationインストール後、Claude Code で/risk-assessment-summarizationを入力して読み込まれたか確認します。
詳細
リスク評価の要約
概要
このスキルは、ServiceNow GRC におけるリスク評価の包括的な要約を作成します。以下を含みます。
- プロファイル、カテゴリ、ビジネスユニット全体のリスクスコアを集計する
- 固有リスクと残余リスクを比較し、統制の有効性を測定する
- 視覚的なレポート用にヒートマップデータ(発生可能性 x 影響度のマトリックス)を生成する
- 時間の経過に伴うリスクの傾向(増加、安定、減少)を特定する
- 対応計画と残余エクスポージャーに基づいて、リスク軽減策を提案する
- リスク評価を、基礎となるリスクイベントおよび統制の有効性と関連付ける
次の場合に使用します。
- リスク委員会または取締役会向けのリスクレポートを作成する場合
- リスク評価サイクルを完了し、結果を統合する必要がある場合
- ビジネスユニット間または期間ごとのリスク状況を比較する場合
- 全社的なリスクエクスポージャーを把握するための戦略計画時
- リスク軽減への投資の有効性を評価する場合
前提条件
- ロール:
sn_risk.manager、sn_grc.manager、またはadmin - プラグイン:
com.sn_risk、com.sn_grc - アクセス権: sn_grc_risk、sn_risk_definition、sn_risk_event、sn_grc_profile テーブルへの読み取りアクセス権
- 知識: リスクスコアリング手法(発生可能性 x 影響度)と組織のリスク許容度に関する理解
主なリスクテーブル
| テーブル | 用途 | 主なフィールド |
|---|---|---|
sn_grc_risk | リスク登録簿のレコード | number, short_description, risk_score, inherent_risk, residual_risk, state, treatment, likelihood, impact, profile, category, owner |
sn_risk_definition | リスクフレームワークの定義 | number, short_description, risk_criteria, scoring_method, appetite |
sn_risk_event | 実際のリスクイベント/インシデント | number, short_description, event_date, impact_amount, risk, category |
sn_grc_profile | リスクのスコープ設定に使用するエンティティプロファイル | number, short_description, profile_type, applies_to |
sn_grc_item | GRC コンテンツ項目 | number, short_description, item_type, profile |
手順
ステップ 1:アクティブなリスクレコードを取得する
スコアリングデータを含むすべてのアクティブなリスクをクエリします。
MCP を使用(Claude Code/Desktop):
Tool: SN-Query-Table
Parameters:
table_name: sn_grc_risk
query: active=true^ORDERBYDESCrisk_score
fields: sys_id,number,short_description,risk_score,inherent_risk,residual_risk,state,treatment,likelihood,impact,profile,category,owner,sys_created_on,sys_updated_on
limit: 100
REST API を使用:
GET /api/now/table/sn_grc_risk?sysparm_query=active=true^ORDERBYDESCrisk_score&sysparm_fields=sys_id,number,short_description,risk_score,inherent_risk,residual_risk,state,treatment,likelihood,impact,profile,category,owner&sysparm_limit=100
ステップ 2:リスクフレームワークの定義を取得する
スコアリング手法とリスク許容度のしきい値を確認します。
MCP を使用:
Tool: SN-Query-Table
Parameters:
table_name: sn_risk_definition
query: active=true
fields: sys_id,number,short_description,risk_criteria,scoring_method,appetite,description
limit: 20
REST API を使用:
GET /api/now/table/sn_risk_definition?sysparm_query=active=true&sysparm_fields=sys_id,number,short_description,risk_criteria,scoring_method,appetite,description&sysparm_limit=20
ステップ 3:固有リスクと残余リスクの分析を含むリスク評価の要約を生成する
集計したリスク指標と統制の有効性を出力します:
Tool: SN-Execute-Background-Script
Parameters:
script: |
var summary = {
generated_date: new GlideDateTime().toString(),
total_risks: 0,
by_treatment: {},
by_category: {},
inherent_vs_residual: {
avg_inherent: 0,
avg_residual: 0,
control_effectiveness_pct: 0,
risks_above_appetite: 0
},
severity_distribution: { critical: 0, high: 0, medium: 0, low: 0 },
top_risks: []
};
var inherentSum = 0, residualSum = 0, count = 0;
var riskAppetite = 50; // Default; adjust per organization
var gr = new GlideRecord('sn_grc_risk');
gr.addQuery('active', true);
gr.orderByDesc('risk_score');
gr.query();
while (gr.next()) {
summary.total_risks++;
count++;
var inherent = parseInt(gr.inherent_risk.toString()) || 0;
var residual = parseInt(gr.residual_risk.toString()) || 0;
var score = parseInt(gr.risk_score.toString()) || 0;
inherentSum += inherent;
residualSum += residual;
if (residual > riskAppetite) summary.inherent_vs_residual.risks_above_appetite++;
// Severity distribution
if (score >= 80) summary.severity_distribution.critical++;
else if (score >= 60) summary.severity_distribution.high++;
else if (score >= 30) summary.severity_distribution.medium++;
else summary.severity_distribution.low++;
// Treatment breakdown
var treat = gr.treatment.getDisplayValue() || 'Not Assigned';
summary.by_treatment[treat] = (summary.by_treatment[treat] || 0) + 1;
// Category breakdown
var cat = gr.category.getDisplayValue() || 'Uncategorized';
summary.by_category[cat] = (summary.by_category[cat] || 0) + 1;
// Top 10 risks
if (summary.top_risks.length < 10) {
summary.top_risks.push({
number: gr.number.toString(),
title: gr.short_description.toString(),
risk_score: score,
inherent: inherent,
residual: residual,
treatment: gr.treatment.getDisplayValue(),
owner: gr.owner.getDisplayValue()
});
}
}
if (count > 0) {
summary.inherent_vs_residual.avg_inherent = Math.round(inherentSum / count);
summary.inherent_vs_residual.avg_residual = Math.round(residualSum / count);
if (inherentSum > 0) {
summary.inherent_vs_residual.control_effectiveness_pct =
Math.round(((inherentSum - residualSum) / inherentSum) * 100);
}
}
gs.info('RISK ASSESSMENT SUMMARY:\n' + JSON.stringify(summary, null, 2));
description: "GRC: Generate risk assessment summary with inherent vs residual analysis"
ステップ 4:ヒートマップデータ(発生可能性 x 影響度のマトリックス)を生成する
ヒートマップの視覚化に使用する発生可能性と影響度のマトリックスを作成します:
Tool: SN-Execute-Background-Script
Parameters:
script: |
// Heat map: 5x5 matrix (likelihood x impact)
var heatMap = {};
for (var l = 1; l <= 5; l++) {
for (var i = 1; i <= 5; i++) {
heatMap[l + 'x' + i] = { likelihood: l, impact: i, count: 0, risks: [] };
}
}
var gr = new GlideRecord('sn_grc_risk');
gr.addQuery('active', true);
gr.query();
while (gr.next()) {
var likelihood = parseInt(gr.likelihood.toString()) || 0;
var impact = parseInt(gr.impact.toString()) || 0;
// Normalize to 1-5 scale if needed
if (likelihood > 5) likelihood = Math.ceil(likelihood / 20);
if (impact > 5) impact = Math.ceil(impact / 20);
if (likelihood < 1) likelihood = 1;
if (impact < 1) impact = 1;
var key = likelihood + 'x' + impact;
if (heatMap[key]) {
heatMap[key].count++;
if (heatMap[key].risks.length < 3) {
heatMap[key].risks.push(gr.number.toString() + ': ' + gr.short_description.toString());
}
}
}
// Format for output
var matrix = [];
for (var cell in heatMap) {
if (heatMap[cell].count > 0) {
matrix.push(heatMap[cell]);
}
}
matrix.sort(function(a, b) { return (b.likelihood * b.impact) - (a.likelihood * a.impact); });
gs.info('RISK HEAT MAP DATA:\n' + JSON.stringify(matrix, null, 2));
gs.info('\nHEAT MAP LEGEND:');
gs.info(' Critical (Red): Likelihood >= 4 AND Impact >= 4');
gs.info(' High (Orange): Score >= 12');
gs.info(' Medium (Yellow): Score >= 6');
gs.info(' Low (Green): Score < 6');
description: "GRC: Generate risk heat map data (likelihood x impact matrix)"
ステップ 5:時間の経過に伴うリスクの傾向を分析する
現在のリスク状況を過去のベースラインと比較します:
Tool: SN-Execute-Background-Script
Parameters:
script: |
var periods = [
{ label: 'Current', days: 0 },
{ label: '30 Days Ago', days: 30 },
{ label: '60 Days Ago', days: 60 },
{ label: '90 Days Ago', days: 90 }
];
var trends = [];
for (var p = 0; p < periods.length; p++) {
var period = periods[p];
var totalScore = 0, count = 0, critical = 0;
if (period.days === 0) {
var gr = new GlideRecord('sn_grc_risk');
gr.addQuery('active', true);
gr.query();
while (gr.next()) {
var s = parseInt(gr.risk_score.toString()) || 0;
totalScore += s;
count++;
if (s >= 80) critical++;
}
} else {
// Use risk events as proxy for historical risk levels
var ev = new GlideAggregate('sn_risk_event');
ev.addQuery('sys_created_on', '>=', gs.daysAgo(period.days));
ev.addQuery('sys_created_on', '<', gs.daysAgo(period.days - 30));
ev.addAggregate('COUNT');
ev.query();
if (ev.next()) count = parseInt(ev.getAggregate('COUNT'));
}
trends.push({
period: period.label,
total_risks: count,
avg_score: count > 0 ? Math.round(totalScore / count) : 0,
critical_risks: critical
});
}
gs.info('RISK TREND ANALYSIS:\n' + JSON.stringify(trends, null, 2));
description: "GRC: Analyze risk score trends over time"
ステップ 6:リスク軽減策を提案する
リスク対応と残余エクスポージャーに基づいて、実行可能な提案を出力します:
Tool: SN-Execute-Background-Script
Parameters:
script: |
var recommendations = [];
var gr = new GlideRecord('sn_grc_risk');
gr.addQuery('active', true);
gr.addQuery('residual_risk', '>', 50);
gr.orderByDesc('residual_risk');
gr.setLimit(20);
gr.query();
while (gr.next()) {
var inherent = parseInt(gr.inherent_risk.toString()) || 0;
var residual = parseInt(gr.residual_risk.toString()) || 0;
var reduction = inherent > 0 ? Math.round(((inherent - residual) / inherent) * 100) : 0;
var treatment = gr.treatment.getDisplayValue() || 'Not Assigned';
var rec = {
risk_number: gr.number.toString(),
title: gr.short_description.toString(),
inherent_risk: inherent,
residual_risk: residual,
risk_reduction_pct: reduction,
current_treatment: treatment,
recommendations: []
};
// Generate recommendations based on treatment and residual level
if (treatment === 'Accept' && residual >= 70) {
rec.recommendations.push('Re-evaluate acceptance decision; residual risk exceeds organizational appetite');
rec.recommendations.push('Conduct cost-benefit analysis for additional controls');
}
if (treatment === 'Mitigate' && reduction < 30) {
rec.recommendations.push('Current controls are insufficient; risk reduction is only ' + reduction + '%');
rec.recommendations.push('Evaluate additional compensating controls or alternative mitigation strategies');
}
if (treatment === 'Transfer' && residual >= 60) {
rec.recommendations.push('Verify insurance coverage adequacy for residual exposure');
rec.recommendations.push('Review third-party SLAs and liability clauses');
}
if (treatment === 'Not Assigned') {
rec.recommendations.push('URGENT: Assign risk treatment strategy immediately');
rec.recommendations.push('Schedule risk owner meeting to determine treatment approach');
}
if (residual >= 80) {
rec.recommendations.push('CRITICAL: Escalate to executive leadership for immediate action');
}
// Check for related control failures
var ctrl = new GlideRecord('sn_compliance_control');
ctrl.addQuery('profile', gr.profile.toString());
ctrl.addQuery('state', 'NOT IN', 'compliant,passed');
ctrl.query();
if (ctrl.getRowCount() > 0) {
rec.recommendations.push('Address ' + ctrl.getRowCount() + ' failing controls linked to this risk profile');
}
recommendations.push(rec);
}
gs.info('MITIGATION RECOMMENDATIONS:\n' + JSON.stringify(recommendations, null, 2));
description: "GRC: Generate mitigation recommendations for high-residual risks"
ステップ 7:損失データの背景情報としてリスクイベントをクエリする
実際のリスクイベントを取得し、実際のインシデントに基づいて評価を行います:
MCP を使用:
Tool: SN-Query-Table
Parameters:
table_name: sn_risk_event
query: sys_created_on>=javascript:gs.daysAgo(365)^ORDERBYDESCimpact_amount
fields: sys_id,number,short_description,event_date,impact_amount,risk,category,state
limit: 50
REST API を使用:
GET /api/now/table/sn_risk_event?sysparm_query=sys_created_on>=javascript:gs.daysAgo(365)^ORDERBYDESCimpact_amount&sysparm_fields=sys_id,number,short_description,event_date,impact_amount,risk,category,state&sysparm_limit=50
ツールの使用方法
| 操作 | MCP ツール | REST エンドポイント |
|---|---|---|
| リスクのクエリ | SN-Query-Table | GET /api/now/table/sn_grc_risk |
| リスク定義のクエリ | SN-Query-Table | GET /api/now/table/sn_risk_definition |
| リスクイベントのクエリ | SN-Query-Table | GET /api/now/table/sn_risk_event |
| リスクの検索 | SN-Natural-Language-Search | N/A |
| 複雑な分析 | SN-Execute-Background-Script | POST /api/now/table/sys_trigger |
| スキーマの検出 | SN-Discover-Table-Schema | GET /api/now/table/sys_dictionary |
ベストプラクティス
- 固有リスクと残余リスク: 管理の効果を示すため、固有リスクと残余リスクの両方を必ず提示します
- リスク許容度の文脈: すべてのスコアを、組織が定めたリスク許容度のしきい値に照らして示します
- ヒートマップの明確さ: レポート全体で一貫した色分けを使用する、標準化された5×5マトリックスを使用します
- トレンドの基準値: 有意義なトレンド比較を行うため、四半期ごとに基準値を設定します
- 対応策の検証: リスク対応策が文書化されているだけでなく、実際に実施されていることを確認します
- 損失データの統合: 評価の精度を検証するため、実際のリスクイベントデータを取り入れます
- オーナーの説明責任: 説明責任を明確にするため、すべての概要にリスクオーナーの名前を含めます
トラブルシューティング
リスクスコアがゼロと表示される
症状: すべてのrisk_score、inherent_risk、またはresidual_riskフィールドが0または空になります
原因: リスクスコアリングでは、評価ワークフローによって入力される計算フィールド、またはカスタムのスコアリングフィールドが使用されている可能性があります
解決策:
Tool: SN-Discover-Table-Schema
Parameters:
table_name: sn_grc_risk
calculated_risk、risk_score_calculated、assessment_scoreなどの代替スコアリングフィールドを確認してください。また、リスク評価が完了していることも確認してください。
発生可能性と影響が入力されていない
症状: 発生可能性/影響の値が空のため、ヒートマップの生成に失敗します
原因: リスクレコードでは、数値スコアではなく定性的な評価(高/中/低)が使用されている可能性があります
解決策: 定性的な値を数値スケールに対応付けます:Critical=5、High=4、Medium=3、Low=2、Very Low=1。フィールド値の定義については、sys_choiceテーブルを確認してください。
リスクイベントテーブルが空である
症状: sn_risk_eventからレコードが返されません
原因: Risk Eventsモジュールが設定されていないか、イベントがカスタムテーブルで追跡されている可能性があります
解決策: com.sn_risk_advancedプラグインが有効になっているか確認してください。損失イベントをカスタムテーブルまたは外部システムで追跡している組織もあります。
例
例1:四半期ごとの全社リスク概要
シナリオ: CROが取締役会向けの統合リスク状況レポートを必要としています
Tool: SN-Query-Table
Parameters:
table_name: sn_grc_risk
query: active=true^risk_score>=70^ORDERBYcategory
limit: 50
生成された概要:
- アクティブなリスクの合計: 156
- リスクの分布: Critical:12 | High:34 | Medium:72 | Low:38
- 固有リスクの平均: 62/100 | 残余リスクの平均: 38/100
- コントロールの有効性: コントロールによるリスク低減率は平均39%
- 許容度超過: 18件のリスクが組織のリスク許容度を超過(しきい値:50)
- 最多カテゴリ: 情報セキュリティ(42件のリスク、27%)
- トレンド: リスク件数は前四半期から8%増加、残余リスクの平均は4%低下
- 主な推奨事項: 「Accept」を対応策とする5件のリスクが許容度を超過しています。受容の判断を再評価してください
例2:リスク委員会向けヒートマップレポート
シナリオ: リスク委員会がリスク状況の視覚的な把握を求めています
ヒートマップ出力(右上が最も懸念される領域):
Impact → 1 2 3 4 5
Likelihood
5 [ 0] [ 2] [ 5] [ 8] [ 3] ← Highest concern row
4 [ 1] [ 4] [12] [15] [ 6]
3 [ 3] [ 8] [18] [10] [ 4]
2 [ 5] [12] [ 9] [ 6] [ 2]
1 [ 8] [ 7] [ 5] [ 2] [ 1]
- 赤ゾーン(L>=4、I>=4): 32件のリスクがあり、経営幹部による即時の対応が必要です
- アンバーゾーン(Score>=12): 47件のリスクが積極的な軽減対応の対象です
- 緑ゾーン(Score<6): 21件のリスクが許容可能な範囲内です
関連スキル
grc/issue-action-plan- 問題として顕在化したリスクに対するアクションプランを作成しますgrc/issue-summarization- リスク評価から生じた問題を要約しますgrc/control-objective-management- 評価済みリスクを軽減するコントロールを管理しますgrc/regulatory-alert-analysis- リスク状況に影響する規制の変更を評価しますsecurity/audit-compliance- リスク軽減を支えるコントロールを監査します