skills/grc/issue-validator/SKILL.md
Validate GRC issues for completeness, accuracy, and compliance. Check required fields, risk ratings, control mappings, and remediation plan adequacy
npx skillsauth add happy-technologies-llc/happy-servicenow-skills issue-validatorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill validates GRC issues in ServiceNow for completeness, accuracy, and compliance readiness. It performs systematic checks across multiple dimensions to ensure issues meet organizational and regulatory standards before audit review.
Key validation capabilities:
When to use:
sn_grc.manager, sn_compliance.manager, or admincom.sn_grc, com.sn_compliance, com.sn_risksn_grc_issue, sn_grc_risk, sn_compliance_control, sn_grc_profile, sn_grc_remediation_task tables| Table | Purpose | Key Fields |
|-------|---------|------------|
| sn_grc_issue | GRC issues and findings | number, short_description, state, priority, risk_rating, profile, item, category, source, assigned_to, due_date |
| sn_grc_risk | Associated risk records | number, risk_score, state, treatment, residual_risk, inherent_risk |
| sn_compliance_control | Mapped controls | number, state, control_objective, effectiveness, test_result |
| sn_grc_profile | Entity profiles | number, profile_type, applies_to |
| sn_grc_remediation_task | Remediation tasks for issues | number, state, assigned_to, due_date, short_description, issue |
Fetch target issues -- either a single issue or a batch for bulk validation.
Using 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
Using 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
Run a comprehensive field completeness check across all target issues.
Using 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"
Using 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
Cross-check issue risk ratings against linked risk record scores to detect mismatches.
Using 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"
Ensure each issue is mapped to at least one control and that mapped controls are in a valid state.
Using 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"
Check that each issue has at least one remediation task with an assigned owner and a due date.
Using 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"
Compile all validation results into a single scored report.
Using 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"
| Operation | MCP Tool | REST Endpoint | |-----------|----------|---------------| | Query Issues | SN-Query-Table | GET /api/now/table/sn_grc_issue | | Read Single Issue | SN-Read-Record | GET /api/now/table/sn_grc_issue/{sys_id} | | Run Validation Scripts | SN-Execute-Background-Script | POST /api/now/table/sys_trigger | | Discover Schema | SN-Discover-Table-Schema | GET /api/now/table/sys_dictionary | | Query Controls | SN-Query-Table | GET /api/now/table/sn_compliance_control |
Symptom: Queries to sn_grc_remediation_task return no results or table not found
Cause: The remediation task table name may differ across ServiceNow versions or custom configurations
Solution:
Tool: SN-Discover-Table-Schema
Parameters:
table_name: sn_grc_issue
Check for reference fields pointing to task tables. Alternative table names include sn_grc_task, task, or custom extended task tables.
Symptom: Risk rating consistency checks produce false positives
Cause: Some organizations use a 1-5 scale while others use 1-100 or Low/Medium/High labels
Solution: Query the sys_choice table for sn_grc_issue.risk_rating to identify the valid values and adjust validation thresholds accordingly.
Symptom: Controls or risks not found when querying by profile sys_id
Cause: The relationship may use item instead of profile, or a many-to-many relationship table
Solution: Check sn_grc_m2m_item_profile or query by the item field on the issue instead of profile.
Scenario: Compliance manager needs to validate all open issues before an external audit
Scenario: Issue owner wants to verify ISSUE0004521 is ready for closure
grc/issue-summarization - Summarize validated issues for reportinggrc/issue-action-plan - Generate action plans for issues failing validationgrc/suggest-remediation-tasks - Create remediation tasks for issues missing themgrc/control-objective-management - Manage controls linked to issuesgrc/risk-assessment-summarization - Deeper risk analysis for rating validationtesting
Manage supplier onboarding, qualification, performance monitoring, and offboarding with auditable lifecycle controls
tools
Identify emerging risks, prioritize intake signals, and route candidates into formal GRC risk assessment workflows
documentation
Screen inbound documents for completeness, policy risk, and routing readiness before extraction or case workflows
testing
Generate concise task summaries with status, timeline, blockers, SLA risk, and recommended next actions