mcpField
I
issue-validator

issue-validator

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

关于 issue-validator

用于 ServiceNow GRC 的议题验证技能,供合规团队在审计或状态变更前检查数据质量。它综合评估字段、风险、控制映射和整改计划,并生成准备度评分。

主要功能

  1. 01检查必填字段完整性
  2. 02核对风险评级与风险记录
  3. 03验证议题与控制的映射
  4. 04检查整改任务和计划
  5. 05生成审计准备度评分报告

适用场景

  1. 01审计委员会报告前批量检查议题
  2. 02定期开展 GRC 数据质量审查
  3. 03关闭议题前核实整改证据

MCP 相关

该技能通过 MCP 工具查询 ServiceNow GRC 议题、风险和控制,并运行后台脚本执行验证与评分。

安装

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

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

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

详细介绍

GRC 问题验证器

概述

此技能用于验证 ServiceNow 中 GRC 问题的完整性、准确性和合规就绪情况。它会从多个维度进行系统检查,确保问题在接受审计审查前符合组织和监管标准。

主要验证功能:

  • 字段完整性: 验证所有必填字段均已填写(简短描述、类别、优先级、风险评级、配置文件、负责人)
  • 风险评级准确性: 将风险评级与关联的风险评分和控制测试结果进行交叉核对
  • 控制映射完整性: 确保问题已正确映射到相关控制和控制目标
  • 整改计划充分性: 验证整改任务是否存在,以及是否具备负责人、截止日期和可衡量的结果
  • 数据一致性: 检测问题严重程度与关联风险/控制状态之间的不匹配
  • 审计就绪情况: 根据就绪度量表为问题评分,以供内部或外部审计展示

使用场景:

  • 向审计委员会报告前,确保数据质量
  • 定期进行 GRC 质量检查时
  • 从自动扫描或评估中导入新问题时
  • 关闭问题前,确认已收集所有整改证据
  • 作为问题管理工作流中的质量关卡

前提条件

  • 角色: 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、sn_grc_remediation_task 表具有读取权限
  • 知识: 了解组织的问题管理政策和必填字段

主要 GRC 表

表用途关键字段
sn_grc_issueGRC 问题和发现项number, short_description, state, priority, risk_rating, profile, item, category, source, assigned_to, due_date
sn_grc_risk关联的风险记录number, risk_score, state, treatment, residual_risk, inherent_risk
sn_compliance_control映射的控制number, state, control_objective, effectiveness, test_result
sn_grc_profile实体配置文件number, profile_type, applies_to
sn_grc_remediation_task问题的整改任务number, state, assigned_to, due_date, short_description, issue

操作步骤

步骤 1:检索待验证的问题

获取目标问题,可以是单个问题,也可以是用于批量验证的一组问题。

使用 MCP(Claude Code/Desktop):

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

使用 REST API:

GET /api/now/table/sn_grc_issue?sysparm_query=active=true^ORDERBYDESCsys_updated_on&sysparm_fields=sys_id,number,short_description,description,state,priority,risk_rating,profile,item,assigned_to,due_date,category,source,remediation_plan&sysparm_limit=50&sysparm_display_value=all
步骤 2:验证字段完整性

对所有目标问题进行全面的字段完整性检查。

使用 MCP:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var requiredFields = [
      'short_description', 'description', 'priority', 'risk_rating',
      'profile', 'assigned_to', 'due_date', 'category', 'source'
    ];
    var results = { total_checked: 0, passed: 0, failed: 0, issues: [] };

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

    while (gr.next()) {
      results.total_checked++;
      var missing = [];

      for (var i = 0; i < requiredFields.length; i++) {
        var field = requiredFields[i];
        if (gr.getValue(field) === null || gr.getValue(field) === '') {
          missing.push(field);
        }
      }

      if (missing.length > 0) {
        results.failed++;
        results.issues.push({
          number: gr.number.toString(),
          title: gr.short_description.toString(),
          missing_fields: missing,
          completeness_pct: Math.round(((requiredFields.length - missing.length) / requiredFields.length) * 100)
        });
      } else {
        results.passed++;
      }
    }

    results.pass_rate = results.total_checked > 0
      ? Math.round((results.passed / results.total_checked) * 100) + '%'
      : 'N/A';

    gs.info('FIELD COMPLETENESS VALIDATION:\n' + JSON.stringify(results, null, 2));
  description: "GRC: Validate field completeness for all active issues"

使用 REST API:

GET /api/now/table/sn_grc_issue?sysparm_query=active=true^short_descriptionISEMPTY^ORassigned_toISEMPTY^ORdue_dateISEMPTY^ORcategoryISEMPTY^ORprofileISEMPTY&sysparm_fields=sys_id,number,short_description,assigned_to,due_date,category,profile&sysparm_limit=100
步骤 3:验证风险评级一致性

将问题的风险评级与关联风险记录的评分进行交叉核对,以检测不匹配。

使用 MCP:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var mismatches = [];
    var gr = new GlideRecord('sn_grc_issue');
    gr.addQuery('active', true);
    gr.addNotNullQuery('risk_rating');
    gr.addNotNullQuery('profile');
    gr.query();

    while (gr.next()) {
      var issueRating = parseInt(gr.risk_rating.toString()) || 0;
      var profileId = gr.profile.toString();

      var risk = new GlideAggregate('sn_grc_risk');
      risk.addQuery('profile', profileId);
      risk.addQuery('active', true);
      risk.addAggregate('MAX', 'residual_risk');
      risk.addAggregate('AVG', 'risk_score');
      risk.query();

      if (risk.next()) {
        var maxResidual = parseInt(risk.getAggregate('MAX', 'residual_risk')) || 0;
        var avgScore = parseInt(risk.getAggregate('AVG', 'risk_score')) || 0;

        // Flag if issue rating is significantly lower than risk data suggests
        if (issueRating <= 2 && maxResidual >= 70) {
          mismatches.push({
            issue: gr.number.toString(),
            issue_risk_rating: issueRating,
            max_residual_risk: maxResidual,
            avg_risk_score: avgScore,
            flag: 'Issue rated LOW but linked risks are HIGH'
          });
        } else if (issueRating >= 4 && maxResidual <= 30) {
          mismatches.push({
            issue: gr.number.toString(),
            issue_risk_rating: issueRating,
            max_residual_risk: maxResidual,
            avg_risk_score: avgScore,
            flag: 'Issue rated HIGH but linked risks are LOW'
          });
        }
      }
    }

    gs.info('RISK RATING CONSISTENCY:\nMismatches found: ' + mismatches.length + '\n' + JSON.stringify(mismatches, null, 2));
  description: "GRC: Cross-validate risk ratings against linked risk records"
步骤 4:验证控制映射

确保每个问题至少映射到一个控制,且映射的控制处于有效状态。

使用 MCP:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var controlIssues = { unmapped: [], stale_controls: [], orphaned: [] };

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

    while (gr.next()) {
      var profileId = gr.profile.toString();
      if (!profileId) {
        controlIssues.unmapped.push({
          issue: gr.number.toString(),
          problem: 'No profile linked - cannot verify control mapping'
        });
        continue;
      }

      var ctrl = new GlideAggregate('sn_compliance_control');
      ctrl.addQuery('profile', profileId);
      ctrl.addAggregate('COUNT');
      ctrl.query();

      var controlCount = 0;
      if (ctrl.next()) {
        controlCount = parseInt(ctrl.getAggregate('COUNT')) || 0;
      }

      if (controlCount === 0) {
        controlIssues.unmapped.push({
          issue: gr.number.toString(),
          profile: gr.profile.getDisplayValue(),
          problem: 'Profile has no linked controls'
        });
      }

      // Check for stale control test results (over 12 months old)
      var stale = new GlideRecord('sn_compliance_control');
      stale.addQuery('profile', profileId);
      stale.addQuery('test_date', '<', gs.daysAgo(365));
      stale.query();
      while (stale.next()) {
        controlIssues.stale_controls.push({
          issue: gr.number.toString(),
          control: stale.number.toString(),
          last_tested: stale.test_date.getDisplayValue()
        });
      }
    }

    gs.info('CONTROL MAPPING VALIDATION:\n' + JSON.stringify(controlIssues, null, 2));
  description: "GRC: Validate control mappings for active issues"
步骤 5:验证整改计划充分性

检查每个问题是否至少有一个整改任务,并且该任务已指定负责人和截止日期。

使用 MCP:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var remediationGaps = [];

    var gr = new GlideRecord('sn_grc_issue');
    gr.addQuery('active', true);
    gr.addQuery('state', 'NOT IN', 'new,draft');
    gr.query();

    while (gr.next()) {
      var tasks = new GlideRecord('sn_grc_remediation_task');
      tasks.addQuery('issue', gr.sys_id.toString());
      tasks.query();

      var taskCount = 0;
      var tasksWithoutOwner = 0;
      var tasksWithoutDueDate = 0;
      var overdueTasks = 0;
      var now = new GlideDateTime();

      while (tasks.next()) {
        taskCount++;
        if (!tasks.assigned_to.toString()) tasksWithoutOwner++;
        if (!tasks.due_date.toString()) tasksWithoutDueDate++;
        if (tasks.due_date.toString() && new GlideDateTime(tasks.due_date.toString()).compareTo(now) < 0 && tasks.state.toString() != 'closed') {
          overdueTasks++;
        }
      }

      var gaps = [];
      if (taskCount === 0) gaps.push('No remediation tasks defined');
      if (tasksWithoutOwner > 0) gaps.push(tasksWithoutOwner + ' task(s) without owner');
      if (tasksWithoutDueDate > 0) gaps.push(tasksWithoutDueDate + ' task(s) without due date');
      if (overdueTasks > 0) gaps.push(overdueTasks + ' overdue task(s)');

      if (gaps.length > 0) {
        remediationGaps.push({
          issue: gr.number.toString(),
          priority: gr.priority.getDisplayValue(),
          task_count: taskCount,
          gaps: gaps
        });
      }
    }

    gs.info('REMEDIATION PLAN VALIDATION:\nIssues with gaps: ' + remediationGaps.length + '\n' + JSON.stringify(remediationGaps, null, 2));
  description: "GRC: Validate remediation plans for active issues"
步骤 6:生成综合验证报告

将所有验证结果汇总为一份统一的评分报告。

使用 MCP:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    var report = {
      generated: new GlideDateTime().toString(),
      summary: { total_issues: 0, fully_valid: 0, needs_attention: 0 },
      scores: [],
      recommendations: []
    };

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

    while (gr.next()) {
      report.summary.total_issues++;
      var score = 100;
      var deductions = [];

      // Field completeness (-10 per missing required field)
      var reqFields = ['short_description','description','priority','risk_rating','profile','assigned_to','due_date','category'];
      for (var i = 0; i < reqFields.length; i++) {
        if (!gr.getValue(reqFields[i])) {
          score -= 10;
          deductions.push('Missing: ' + reqFields[i]);
        }
      }

      // Due date validation (-15 if overdue)
      if (gr.due_date.toString() && new GlideDateTime(gr.due_date.toString()).compareTo(new GlideDateTime()) < 0) {
        score -= 15;
        deductions.push('Issue is overdue');
      }

      // Remediation check (-20 if no tasks)
      var taskGr = new GlideAggregate('sn_grc_remediation_task');
      taskGr.addQuery('issue', gr.sys_id.toString());
      taskGr.addAggregate('COUNT');
      taskGr.query();
      var taskCount = 0;
      if (taskGr.next()) taskCount = parseInt(taskGr.getAggregate('COUNT'));
      if (taskCount === 0) {
        score -= 20;
        deductions.push('No remediation tasks');
      }

      if (score < 0) score = 0;

      if (score >= 80) report.summary.fully_valid++;
      else report.summary.needs_attention++;

      if (score < 80) {
        report.scores.push({
          issue: gr.number.toString(),
          title: gr.short_description.toString(),
          validation_score: score,
          deductions: deductions
        });
      }
    }

    report.scores.sort(function(a, b) { return a.validation_score - b.validation_score; });

    gs.info('GRC ISSUE VALIDATION REPORT:\n' + JSON.stringify(report, null, 2));
  description: "GRC: Generate comprehensive validation report with scores"

工具用法

操作MCP 工具REST 端点
查询问题SN-Query-TableGET /api/now/table/sn_grc_issue
读取单个问题SN-Get-RecordGET /api/now/table/sn_grc_issue/{sys_id}
运行验证脚本SN-Execute-Background-ScriptPOST /api/now/table/sys_trigger
探索架构SN-Discover-Table-SchemaGET /api/now/table/sys_dictionary
查询控制SN-Query-TableGET /api/now/table/sn_compliance_control

最佳实践

  • 定期运行验证: 安排每周或每两周进行一次全面验证,而不只是审计前验证
  • 集中定义验证规则: 在共享的合规标准中记录必填字段和阈值,确保验证人员使用一致的标准
  • 评分并确定优先级: 使用验证评分确定数据质量问题修复的优先级——优先解决关键缺口
  • 尽可能自动化: 将重复的验证检查转化为业务规则或定时作业,以便持续监控
  • 跟踪趋势: 监控验证通过率随时间的变化,以衡量数据质量的改善情况
  • 状态转换前进行验证: 在问题转为“In Remediation”或“Closed”状态前强制执行验证检查
  • 在报告中提供背景信息: 始终为验证失败项附上可执行的修复指导

故障排除

未找到修复任务表

症状: 查询 sn_grc_remediation_task 无结果,或提示找不到表 原因: 不同的 ServiceNow 版本或自定义配置可能使用不同的修复任务表名 解决方案:

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

检查指向任务表的引用字段。其他可能的表名包括 sn_grc_task、task 或自定义扩展任务表。

风险评级字段使用不同的量表

症状: 风险评级一致性检查产生误报 原因: 有些组织使用 1-5 量表,其他组织则使用 1-100 量表或 Low/Medium/High 标签 解决方案: 查询 sys_choice 表中的 sn_grc_issue.risk_rating,以确定有效值,并相应调整验证阈值。

基于档案的查询未返回关联记录

症状: 按档案 sys_id 查询时找不到控制措施或风险 原因: 关联关系可能使用 item 而非 profile,也可能通过多对多关系表建立 解决方案: 检查 sn_grc_m2m_item_profile,或改为按问题上的 item 字段查询,而非 profile。

示例

示例 1:审计前批量验证

场景: 合规经理需要在外部审计前验证所有未解决的问题

  1. 查询全部 85 条有效的 GRC 问题
  2. 运行字段完整性检查:72 条通过(85%),13 条未通过
  3. 运行风险评级一致性检查:标记出 4 处不一致
  4. 运行控制措施映射检查:6 个问题未关联控制措施
  5. 运行修复计划检查:11 个问题缺少修复任务
  6. 生成评分报告:62 个问题得分达到 80 分以上,23 个需要关注
  7. 首要发现:5 个最高优先级问题没有指定负责人
示例 2:关闭前验证单个问题

场景: 问题负责人想确认 ISSUE0004521 是否已满足关闭条件

  1. 读取包含所有字段的问题记录
  2. 字段完整性:100%——所有必填字段均已填写
  3. 风险评级:评级为“High”(4),关联风险的剩余分数为 72——一致
  4. 控制措施映射:关联了 3 项控制措施,均处于“Compliant”状态
  5. 修复情况:2 项任务均已关闭,并附有完成证据
  6. 验证评分:95/100(轻微扣分:关闭备注可以写得更详细)
  7. 结果:批准关闭问题

相关技能

  • grc/issue-summarization - 汇总已验证的问题以供报告
  • grc/issue-action-plan - 为未通过验证的问题生成行动计划
  • grc/suggest-remediation-tasks - 为缺少修复任务的问题创建修复任务
  • grc/control-objective-management - 管理与问题关联的控制措施
  • grc/risk-assessment-summarization - 深入分析风险,以验证评级

评论

支持 Markdown