| name | grc-compliance |
| description | Build ServiceNow GRC — sn_compliance_policy lifecycle, sn_compliance_control tests, sn_risk_risk assessment with inherent/residual scoring, audit engagements, and findings remediation. |
| license | Apache-2.0 |
| compatibility | Designed for Snow-Code and ServiceNow development |
| metadata | {"author":"serac","version":"1.0.0","category":"servicenow"} |
| tools | ["snow_query_table","snow_execute_script","snow_run_compliance_scan","snow_grc_risk_manage (action='assess')"] |
GRC & Compliance for ServiceNow
GRC (Governance, Risk, Compliance) manages organizational policies, risks, and regulatory compliance.
GRC Architecture
Policy (sn_compliance_policy)
└── Policy Statements
└── Controls (sn_compliance_control)
└── Control Tests
└── Test Results
Risk (sn_risk_risk)
├── Risk Assessment
└── Risk Response
Key Tables
| Table | Purpose |
|---|
sn_compliance_policy | Compliance policies |
sn_compliance_control | Controls |
sn_compliance_control_test | Control tests |
sn_risk_risk | Risk records |
sn_audit_engagement | Audit engagements |
Policies (ES5)
Create Policy
var policy = new GlideRecord("sn_compliance_policy")
policy.initialize()
policy.setValue("name", "Information Security Policy")
policy.setValue("description", "Enterprise information security requirements")
policy.setValue("short_description", "InfoSec Policy")
policy.setValue("category", "security")
policy.setValue("type", "corporate")
policy.setValue("owner", policyOwnerSysId)
policy.setValue("owning_group", securityTeamSysId)
policy.setValue("state", "draft")
policy.setValue("effective_date", "2024-01-01")
policy.setValue("review_date", "2025-01-01")
policy.insert()
Policy Lifecycle
function transitionPolicy(policySysId, newState, notes) {
var policy = new GlideRecord("sn_compliance_policy")
if (!policy.get(policySysId)) {
return { success: false, message: "Policy not found" }
}
var validTransitions = {
draft: ["review", "retired"],
review: ["approved", "draft"],
approved: ["published", "draft"],
published: ["review", "retired"],
retired: ["draft"],
}
var currentState = policy.getValue("state")
if (!validTransitions[currentState] || validTransitions[currentState].indexOf(newState) === -1) {
return { success: false, message: "Invalid transition" }
}
policy.setValue("state", newState)
if (newState === "published") {
policy.setValue("published_date", new GlideDateTime())
}
if (notes) {
policy.work_notes = notes
}
policy.update()
return { success: true, state: newState }
}
Controls (ES5)
Create Control
var control = new GlideRecord("sn_compliance_control")
control.initialize()
control.setValue("name", "Access Control Review")
control.setValue("description", "Quarterly review of user access rights")
control.setValue("short_description", "Access Review Control")
control.setValue("policy", policySysId)
control.setValue("type", "detective")
control.setValue("category", "access_control")
control.setValue("frequency", "quarterly")
control.setValue("owner", controlOwnerSysId)
control.setValue("test_frequency", "quarterly")
control.setValue("test_type", "manual")
control.setValue("state", "draft")
control.insert()
Control Testing
function createControlTest(controlSysId, testData) {
var test = new GlideRecord("sn_compliance_control_test")
test.initialize()
test.setValue("control", controlSysId)
test.setValue("name", testData.name)
test.setValue("description", testData.description)
test.setValue("test_type", testData.type)
test.setValue("planned_start", testData.plannedStart)
test.setValue("planned_end", testData.plannedEnd)
test.setValue("assigned_to", testData.tester)
test.setValue("state", "open")
return test.insert()
}
function recordTestResult(testSysId, result) {
var test = new GlideRecord("sn_compliance_control_test")
if (!test.get(testSysId)) {
return false
}
test.setValue("state", "closed")
test.setValue("result", result.outcome)
test.setValue("actual_end", new GlideDateTime())
test.setValue("findings", result.findings)
test.setValue("evidence", result.evidence)
if (result.outcome === "fail") {
createComplianceIssue(test, result)
}
test.update()
return true
}
Risk Management (ES5)
Create Risk
var risk = new GlideRecord("sn_risk_risk")
risk.initialize()
risk.setValue("name", "Data Breach Risk")
risk.setValue("description", "Risk of unauthorized access to customer data")
risk.setValue("short_description", "Data Breach")
risk.setValue("category", "security")
risk.setValue("subcategory", "data_protection")
risk.setValue("inherent_likelihood", 3)
risk.setValue("inherent_impact", 5)
risk.setValue("controls", controlSysIds)
risk.setValue("residual_likelihood", 2)
risk.setValue("residual_impact", 5)
risk.setValue("owner", riskOwnerSysId)
risk.setValue("state", "assess")
risk.insert()
Risk Assessment
function calculateRiskScore(likelihood, impact) {
var score = likelihood * impact
var rating = "low"
if (score >= 20) {
rating = "critical"
} else if (score >= 12) {
rating = "high"
} else if (score >= 6) {
rating = "medium"
}
return {
score: score,
rating: rating,
}
}
function assessRisk(riskSysId, assessment) {
var risk = new GlideRecord("sn_risk_risk")
if (!risk.get(riskSysId)) {
return false
}
risk.setValue("inherent_likelihood", assessment.inherentLikelihood)
risk.setValue("inherent_impact", assessment.inherentImpact)
var inherentScore = calculateRiskScore(assessment.inherentLikelihood, assessment.inherentImpact)
risk.setValue("inherent_risk_score", inherentScore.score)
risk.setValue("inherent_risk_rating", inherentScore.rating)
risk.setValue("residual_likelihood", assessment.residualLikelihood)
risk.setValue("residual_impact", assessment.residualImpact)
var residualScore = calculateRiskScore(assessment.residualLikelihood, assessment.residualImpact)
risk.setValue("residual_risk_score", residualScore.score)
risk.setValue("residual_risk_rating", residualScore.rating)
risk.setValue("assessed_date", new GlideDateTime())
risk.setValue("assessed_by", gs.getUserID())
risk.setValue("state", "monitor")
risk.update()
return true
}
Audits (ES5)
Create Audit Engagement
var audit = new GlideRecord("sn_audit_engagement")
audit.initialize()
audit.setValue("name", "Q1 2024 SOX Audit")
audit.setValue("description", "Quarterly SOX compliance audit")
audit.setValue("type", "compliance")
audit.setValue("planned_start", "2024-01-15")
audit.setValue("planned_end", "2024-02-15")
audit.setValue("scope", "Financial controls, access management")
audit.setValue("lead_auditor", auditorSysId)
audit.setValue("audit_team", auditTeamSysId)
audit.setValue("state", "planning")
audit.insert()
Audit Findings
function createAuditFinding(auditSysId, findingData) {
var finding = new GlideRecord("sn_audit_finding")
finding.initialize()
finding.setValue("engagement", auditSysId)
finding.setValue("title", findingData.title)
finding.setValue("description", findingData.description)
finding.setValue("severity", findingData.severity)
if (findingData.control) {
finding.setValue("control", findingData.control)
}
finding.setValue("recommendation", findingData.recommendation)
finding.setValue("owner", findingData.owner)
finding.setValue("due_date", findingData.dueDate)
finding.setValue("state", "open")
return finding.insert()
}
Compliance Reporting (ES5)
Compliance Dashboard Data
function getComplianceSummary() {
var summary = {
policies: { total: 0, published: 0, review_needed: 0 },
controls: { total: 0, effective: 0, failed: 0 },
risks: { critical: 0, high: 0, medium: 0, low: 0 },
audits: { open: 0, findings: 0 },
}
var ga = new GlideAggregate("sn_compliance_policy")
ga.addAggregate("COUNT")
ga.groupBy("state")
ga.query()
while (ga.next()) {
var count = parseInt(ga.getAggregate("COUNT"), 10)
summary.policies.total += count
if (ga.getValue("state") === "published") {
summary.policies.published = count
}
}
ga = new GlideAggregate("sn_risk_risk")
ga.addQuery("active", true)
ga.addAggregate("COUNT")
ga.groupBy("residual_risk_rating")
ga.query()
while (ga.next()) {
var rating = ga.getValue("residual_risk_rating")
var riskCount = parseInt(ga.getAggregate("COUNT"), 10)
if (summary.risks.hasOwnProperty(rating)) {
summary.risks[rating] = riskCount
}
}
return summary
}
MCP Tool Integration
Available Tools
| Tool | Purpose |
|---|
snow_query_table | Query GRC tables |
snow_execute_script | Test GRC scripts |
snow_run_compliance_scan | Run compliance audits |
snow_grc_risk_manage (action='assess') | Risk assessment |
Example Workflow
await snow_query_table({
table: "sn_compliance_policy",
query: "state=published",
fields: "name,category,effective_date,review_date",
})
await snow_query_table({
table: "sn_risk_risk",
query: "residual_risk_rating=high^ORresidual_risk_rating=critical",
fields: "name,category,residual_risk_score,owner",
})
await snow_execute_script({
script: `
var summary = getComplianceSummary();
gs.info(JSON.stringify(summary));
`,
})
Best Practices
- Clear Ownership - Assign policy/control/risk owners
- Regular Reviews - Schedule periodic assessments
- Evidence Collection - Document control effectiveness
- Risk Quantification - Use consistent scoring
- Audit Trail - Track all changes
- Automation - Automate testing where possible
- Reporting - Regular compliance dashboards
- ES5 Only - No modern JavaScript syntax