mcpField
I
issue-summarization

issue-summarization

github.com/happy-technologies-llc安全4141 次安装2 天前更新
代码仓库

关于 issue-summarization

用于汇总 ServiceNow GRC 议题的技能,面向合规团队、审计人员及管理层报告场景。它关联风险、控制和合规缺口,生成议题详情、趋势指标及适用于审计委员会的摘要。

主要功能

  1. 01汇总议题及关联风险和控制
  2. 02分析业务影响与合规缺口
  3. 03生成管理层摘要和趋势指标
  4. 04编写关联法规引用的叙述

适用场景

  1. 01准备审计委员会或董事会简报
  2. 02开展季度或年度合规审查
  3. 03汇总内外部审计发现

MCP 相关

该技能使用 MCP 工具查询 ServiceNow GRC 议题、风险和控制,并通过后台脚本汇总指标、生成报告内容。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/happy-technologies-llc/happy-platform-skills --skill issue-summarization

安装后在 Claude Code 中输入/issue-summarization确认已加载。

详细介绍

GRC 问题摘要

概述

此技能可生成 ServiceNow 中治理、风险与合规(GRC)问题的上下文摘要。涵盖:

  • 汇总 GRC 问题数据及相关风险、控制措施和合规差距
  • 通过将问题与配置文件和政策违规相关联,评估业务影响
  • 生成适用于审计委员会和董事会报告的高管摘要
  • 生成趋势分析,展示问题数量、未解决时长和解决率
  • 生成与法规引用相关联的合规差距说明

适用场景:

  • 准备高管简报或审计委员会报告时
  • 利益相关者需要全面了解 GRC 状况时
  • 季度或年度合规审查期间
  • 总结内部或外部审计发现时
  • 需要概览问题状态的风险委员会会议前

前提条件

  • 角色: sn_grc.viewer、sn_grc.manager、sn_compliance.manager 或 admin
  • 插件: com.sn_grc、com.sn_compliance、com.sn_risk
  • 访问权限: 对 sn_grc_issue、sn_grc_risk、sn_compliance_control、sn_grc_profile 表具有读取权限
  • 知识: 了解组织的合规框架和报告要求

关键 GRC 表

表用途关键字段
sn_grc_issueGRC 问题和发现number, short_description, state, priority, risk_rating, profile, item, category, source
sn_grc_risk与配置文件关联的风险记录number, short_description, risk_score, state, treatment, residual_risk, inherent_risk
sn_compliance_control合规控制措施number, short_description, state, control_objective, owner, effectiveness, test_result
sn_compliance_policy合规政策number, short_description, state, owner, policy_statement, effective_date
sn_grc_profileGRC 实体配置文件number, short_description, profile_type, applies_to
sn_compliance_citation法规引用number, short_description, reference, regulation, authority

操作步骤

第 1 步:检索要汇总的 GRC 问题

按范围筛选 GRC 问题(所有未关闭问题、特定类别或日期范围)。

使用 MCP(Claude Code/Desktop):

Tool: SN-Query-Table
Parameters:
  table_name: sn_grc_issue
  query: active=true^ORDERBYDESCpriority
  fields: sys_id,number,short_description,description,state,priority,risk_rating,profile,item,assigned_to,due_date,category,source,sys_created_on,sys_updated_on,closed_at
  limit: 100

使用 REST API:

GET /api/now/table/sn_grc_issue?sysparm_query=active=true^ORDERBYDESCpriority&sysparm_fields=sys_id,number,short_description,description,state,priority,risk_rating,profile,item,assigned_to,due_date,category,source&sysparm_limit=100
第 2 步:收集相关风险背景

针对每个问题,提取关联的风险记录,以了解风险状况。

使用 MCP:

Tool: SN-Query-Table
Parameters:
  table_name: sn_grc_risk
  query: profile=[issue_profile_sys_id]^active=true
  fields: sys_id,number,short_description,risk_score,state,treatment,residual_risk,inherent_risk,category,owner
  limit: 50

使用 REST API:

GET /api/now/table/sn_grc_risk?sysparm_query=profile=[profile_sys_id]^active=true&sysparm_fields=sys_id,number,short_description,risk_score,state,treatment,residual_risk,inherent_risk,category&sysparm_limit=50
第 3 步:识别合规差距和控制失效

查询与问题配置文件关联、处于不合规状态的控制措施。

使用 MCP:

Tool: SN-Query-Table
Parameters:
  table_name: sn_compliance_control
  query: profile=[issue_profile_sys_id]^stateNOT INcompliant,passed^active=true
  fields: sys_id,number,short_description,state,control_objective,owner,effectiveness,test_result,test_date
  limit: 50

使用 REST API:

GET /api/now/table/sn_compliance_control?sysparm_query=profile=[profile_sys_id]^stateNOT INcompliant,passed^active=true&sysparm_fields=sys_id,number,short_description,state,control_objective,owner,effectiveness,test_result&sysparm_limit=50
第 4 步:生成包含汇总指标的高管摘要

生成包含统计数据和趋势的全面摘要:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var summary = {
      generated_date: new GlideDateTime().toString(),
      issue_overview: { total_open: 0, critical: 0, high: 0, medium: 0, low: 0, overdue: 0 },
      by_category: {},
      by_source: {},
      risk_context: { avg_risk_score: 0, max_risk_score: 0, total_risks: 0 },
      compliance_gaps: { failing_controls: 0, untested_controls: 0 },
      aging: { under_30_days: 0, days_30_to_60: 0, days_60_to_90: 0, over_90_days: 0 },
      resolution_rate: { closed_last_30: 0, opened_last_30: 0 }
    };

    var now = new GlideDateTime();
    var gr = new GlideRecord('sn_grc_issue');
    gr.addQuery('active', true);
    gr.query();

    var riskScores = [];
    while (gr.next()) {
      summary.issue_overview.total_open++;

      // Priority distribution
      var pri = gr.priority.toString();
      if (pri == '1') summary.issue_overview.critical++;
      else if (pri == '2') summary.issue_overview.high++;
      else if (pri == '3') summary.issue_overview.medium++;
      else summary.issue_overview.low++;

      // Overdue check
      if (gr.due_date.toString() && new GlideDateTime(gr.due_date.toString()).compareTo(now) < 0) {
        summary.issue_overview.overdue++;
      }

      // Category breakdown
      var cat = gr.category.getDisplayValue() || 'Uncategorized';
      summary.by_category[cat] = (summary.by_category[cat] || 0) + 1;

      // Source breakdown
      var src = gr.source.getDisplayValue() || 'Manual';
      summary.by_source[src] = (summary.by_source[src] || 0) + 1;

      // Aging analysis
      var created = new GlideDateTime(gr.sys_created_on.toString());
      var ageDays = gs.dateDiff(created.toString(), now.toString(), true);
      if (ageDays < 30) summary.aging.under_30_days++;
      else if (ageDays < 60) summary.aging.days_30_to_60++;
      else if (ageDays < 90) summary.aging.days_60_to_90++;
      else summary.aging.over_90_days++;

      // Collect risk scores from related risks
      var risk = new GlideRecord('sn_grc_risk');
      risk.addQuery('profile', gr.profile.toString());
      risk.addQuery('active', true);
      risk.query();
      while (risk.next()) {
        var score = parseInt(risk.risk_score.toString()) || 0;
        riskScores.push(score);
        summary.risk_context.total_risks++;
        if (score > summary.risk_context.max_risk_score) summary.risk_context.max_risk_score = score;
      }
    }

    // Average risk score
    if (riskScores.length > 0) {
      var total = 0;
      for (var i = 0; i < riskScores.length; i++) total += riskScores[i];
      summary.risk_context.avg_risk_score = Math.round(total / riskScores.length);
    }

    // Compliance gap count
    var ctrl = new GlideAggregate('sn_compliance_control');
    ctrl.addQuery('active', true);
    ctrl.addQuery('state', 'NOT IN', 'compliant,passed');
    ctrl.addAggregate('COUNT');
    ctrl.query();
    if (ctrl.next()) summary.compliance_gaps.failing_controls = parseInt(ctrl.getAggregate('COUNT'));

    // Resolution rate (last 30 days)
    var closed = new GlideAggregate('sn_grc_issue');
    closed.addQuery('closed_at', '>=', gs.daysAgo(30));
    closed.addAggregate('COUNT');
    closed.query();
    if (closed.next()) summary.resolution_rate.closed_last_30 = parseInt(closed.getAggregate('COUNT'));

    var opened = new GlideAggregate('sn_grc_issue');
    opened.addQuery('sys_created_on', '>=', gs.daysAgo(30));
    opened.addAggregate('COUNT');
    opened.query();
    if (opened.next()) summary.resolution_rate.opened_last_30 = parseInt(opened.getAggregate('COUNT'));

    gs.info('EXECUTIVE GRC ISSUE SUMMARY:\n' + JSON.stringify(summary, null, 2));
  description: "GRC: Generate executive summary of open issues with metrics"
第 5 步:生成单个问题的详细摘要

为特定问题生成深入分析摘要:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var issueNumber = 'ISSUE0001234'; // Replace with target issue
    var gr = new GlideRecord('sn_grc_issue');
    gr.addQuery('number', issueNumber);
    gr.query();

    if (gr.next()) {
      var detail = {
        issue: {
          number: gr.number.toString(),
          title: gr.short_description.toString(),
          description: gr.description.toString(),
          state: gr.state.getDisplayValue(),
          priority: gr.priority.getDisplayValue(),
          risk_rating: gr.risk_rating.toString(),
          category: gr.category.getDisplayValue(),
          source: gr.source.getDisplayValue(),
          assigned_to: gr.assigned_to.getDisplayValue(),
          profile: gr.profile.getDisplayValue(),
          created: gr.sys_created_on.toString(),
          due_date: gr.due_date.toString()
        },
        related_risks: [],
        affected_controls: [],
        policy_references: [],
        business_impact: ''
      };

      // Related risks
      var risk = new GlideRecord('sn_grc_risk');
      risk.addQuery('profile', gr.profile.toString());
      risk.addQuery('active', true);
      risk.query();
      while (risk.next()) {
        detail.related_risks.push({
          number: risk.number.toString(),
          title: risk.short_description.toString(),
          inherent_risk: risk.inherent_risk.toString(),
          residual_risk: risk.residual_risk.toString(),
          treatment: risk.treatment.getDisplayValue()
        });
      }

      // Affected controls
      var ctrl = new GlideRecord('sn_compliance_control');
      ctrl.addQuery('profile', gr.profile.toString());
      ctrl.query();
      while (ctrl.next()) {
        detail.affected_controls.push({
          number: ctrl.number.toString(),
          title: ctrl.short_description.toString(),
          state: ctrl.state.getDisplayValue(),
          effectiveness: ctrl.effectiveness.getDisplayValue(),
          owner: ctrl.owner.getDisplayValue()
        });
      }

      // Policy references via profile
      var policy = new GlideRecord('sn_compliance_policy');
      policy.addQuery('active', true);
      policy.query();
      while (policy.next()) {
        detail.policy_references.push({
          number: policy.number.toString(),
          title: policy.short_description.toString(),
          state: policy.state.getDisplayValue()
        });
      }

      // Determine business impact narrative
      var maxRisk = 0;
      for (var i = 0; i < detail.related_risks.length; i++) {
        var rs = parseInt(detail.related_risks[i].residual_risk) || 0;
        if (rs > maxRisk) maxRisk = rs;
      }
      var failingCtrls = detail.affected_controls.filter(function(c) {
        return c.state !== 'Compliant' && c.state !== 'Passed';
      }).length;

      if (maxRisk >= 80 || failingCtrls >= 3) {
        detail.business_impact = 'HIGH - Significant compliance exposure with elevated residual risk and multiple control failures';
      } else if (maxRisk >= 50 || failingCtrls >= 1) {
        detail.business_impact = 'MEDIUM - Moderate compliance concern requiring timely remediation';
      } else {
        detail.business_impact = 'LOW - Minor finding with limited business impact';
      }

      gs.info('ISSUE DETAIL SUMMARY:\n' + JSON.stringify(detail, null, 2));
    }
  description: "GRC: Generate detailed summary for a specific issue"
第 6 步:生成审计委员会说明

使用编码后的全文查询按主题查找问题:

Tool: SN-Query-Table
Parameters:
  table_name: sn_grc_issue
  query: priorityIN1,2^sys_created_on>=javascript:gs.monthsAgoStart(3)^123TEXTQUERY321=data privacy access controls
  limit: 25

生成格式化说明:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var narrative = [];
    narrative.push('=== GRC ISSUE SUMMARY FOR AUDIT COMMITTEE ===');
    narrative.push('Report Date: ' + new GlideDateTime().getDisplayValue());
    narrative.push('');

    // Open issue statistics
    var agg = new GlideAggregate('sn_grc_issue');
    agg.addQuery('active', true);
    agg.addAggregate('COUNT');
    agg.groupBy('priority');
    agg.query();

    narrative.push('OPEN ISSUES BY PRIORITY:');
    while (agg.next()) {
      narrative.push('  Priority ' + agg.priority.getDisplayValue() + ': ' + agg.getAggregate('COUNT') + ' issues');
    }
    narrative.push('');

    // Overdue issues
    var overdue = new GlideAggregate('sn_grc_issue');
    overdue.addQuery('active', true);
    overdue.addQuery('due_date', '<', new GlideDateTime().toString());
    overdue.addAggregate('COUNT');
    overdue.query();
    if (overdue.next()) {
      narrative.push('OVERDUE ISSUES: ' + overdue.getAggregate('COUNT'));
    }
    narrative.push('');

    // Top 5 critical issues
    narrative.push('TOP CRITICAL ISSUES:');
    var top = new GlideRecord('sn_grc_issue');
    top.addQuery('active', true);
    top.addQuery('priority', 'IN', '1,2');
    top.orderByDesc('risk_rating');
    top.setLimit(5);
    top.query();
    var idx = 1;
    while (top.next()) {
      narrative.push('  ' + idx + '. ' + top.number + ' - ' + top.short_description);
      narrative.push('     Risk Rating: ' + top.risk_rating + ' | Due: ' + top.due_date.getDisplayValue());
      idx++;
    }

    gs.info(narrative.join('\n'));
  description: "GRC: Generate audit committee narrative report"

工具用法

操作MCP 工具REST 端点
查询问题SN-Query-TableGET /api/now/table/sn_grc_issue
搜索问题SN-Natural-Language-SearchN/A
查询风险SN-Query-TableGET /api/now/table/sn_grc_risk
查询控制措施SN-Query-TableGET /api/now/table/sn_compliance_control
汇总分析SN-Execute-Background-ScriptPOST /api/now/table/sys_trigger
架构发现SN-Discover-Table-SchemaGET /api/now/table/sys_dictionary

最佳实践

  • 了解受众: 根据受众调整摘要的详略程度——高管需要高级指标,合规团队需要控制层级的详细信息
  • 趋势背景: 始终包含趋势数据(月环比、季度环比),以展示发展态势
  • 风险关联: 将问题关联到其根本风险,以展示根因模式
  • 逾期情况可见性: 在高管摘要中醒目地突出逾期问题和逾期时长区间
  • 法规映射: 在可用时,将问题映射到具体法规要求或条款
  • 提供可执行的建议: 摘要应包含建议的后续步骤,而不仅是状态说明
  • 数据时效性: 包含报告生成时间戳和数据时效性免责声明

故障排除

问题的风险上下文为空

症状: 按配置文件查询时,问题未返回相关风险
原因: 问题可能未关联到 GRC 配置文件,或配置文件关系使用了其他字段
解决方案:

Tool: SN-Discover-Table-Schema
Parameters:
  table_name: sn_grc_issue

检查是否存在其他关系字段,例如 item、content 或用于关联风险记录的自定义引用字段。

类别字段返回空值

症状: 问题类别明细中所有项目都显示为“未分类”
原因: category 字段可能未填充,或使用了其他字段名称
解决方案: 查询 sn_grc_issue 的 sys_choice 表,以验证可用的类别值,并检查是否改用 issue_type 等自定义字段。

聚合查询超时

症状: 大型数据集上的 GlideAggregate 后台脚本超时
原因: 问题记录数量庞大,且未进行适当索引
解决方案: 添加日期范围筛选器以缩小数据集,或在聚合循环中的明细查询上使用 setLimit()。

示例

示例 1:月度合规态势摘要

场景: 合规经理需要为风险委员会准备月度概览

Tool: SN-Query-Table
Parameters:
  table_name: sn_grc_issue
  query: active=true^priorityIN1,2^sys_created_on>=javascript:gs.beginningOfThisMonth()^ORsys_updated_on>=javascript:gs.beginningOfThisMonth()
  limit: 50

生成的摘要:

  • 未解决问题总数: 47(上月为 42)
  • 严重/高风险: 12 个需要立即处理的问题
  • 逾期: 8 个问题已超过截止日期(逾期率为 17%)
  • 首要类别: 访问控制(15 个问题,占 32%)
  • 平均风险分数: 相关风险记录的平均分数为 67/100
  • 控制失效: 9 项控制处于不合规状态
  • 建议: 将 3 个逾期的严重问题升级给执行负责人;安排 Q2 控制重新测试
示例 2:审计用单个问题深入分析

场景: 外部审计员要求了解某项具体发现的详细背景

Tool: SN-Query-Table
Parameters:
  table_name: sn_grc_issue
  query: number=ISSUE0002341
  fields: sys_id,number,short_description,description,state,priority,risk_rating,profile,item,assigned_to,due_date,category,source,work_notes,sys_created_on
  limit: 1

生成的详细信息:

  • 问题: ISSUE0002341 - 静态数据加密不足
  • 来源: 外部审计发现
  • 风险评级: 4(严重)
  • 相关风险: 2 项风险,平均剩余风险分数为 75
  • 受影响的控制: CTRL0001892(失败)、CTRL0001893(未测试)
  • 政策参考: POL0000145 - 数据保护政策
  • 业务影响: 高——依据 GDPR 第 32 条,存在监管风险;潜在罚款最高可达年收入的 4%
  • 未解决天数: 45(已超过截止日期 15 天)

相关技能

  • grc/issue-action-plan - 为摘要中的问题生成整改行动计划
  • grc/risk-assessment-summarization - 深入分析问题背后的风险
  • grc/control-objective-management - 管理与问题关联的控制
  • grc/regulatory-alert-analysis - 识别问题背后的监管因素
  • security/audit-compliance - 审计跟踪和合规报告

参考资料

评论

支持 Markdown