instance-security
SKILL.md
Instance Security for ServiceNow
Instance Security covers authentication, authorization, and security hardening.
Security Layers
Network Security
↓
Authentication (SSO, MFA)
↓
Session Management
↓
Authorization (ACLs, Roles)
↓
Data Protection
↓
Audit & Logging
Key Tables
| Table | Purpose |
|---|---|
sys_user |
User accounts |
sys_user_role |
Roles |
sys_security_acl |
Access controls |
sysevent_log |
Security events |
sys_properties |
Security settings |
Authentication Security (ES5)
Password Policy Configuration
// Check password strength (ES5 ONLY!)
function validatePasswordStrength(password) {
var issues = []
// Minimum length
var minLength = parseInt(gs.getProperty("glide.security.password.min_length", "8"), 10)
if (password.length < minLength) {
issues.push("Password must be at least " + minLength + " characters")
}
// Complexity requirements
if (!/[A-Z]/.test(password)) {
issues.push("Password must contain uppercase letter")
}
if (!/[a-z]/.test(password)) {
issues.push("Password must contain lowercase letter")
}
if (!/[0-9]/.test(password)) {
issues.push("Password must contain number")
}
if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
issues.push("Password must contain special character")
}
return {
valid: issues.length === 0,
issues: issues,
}
}
Session Security
// Check session validity (ES5 ONLY!)
function isSessionSecure() {
var session = gs.getSession()
// Check session age
var maxAge = parseInt(gs.getProperty("glide.security.session.timeout", "60"), 10)
var sessionAge = session.getSessionAge() / 60000 // Convert to minutes
if (sessionAge > maxAge) {
return { valid: false, reason: "Session expired" }
}
// Check for session fixation
var clientIP = gs.getSession().getClientIP()
var originalIP = session.getValue("original_ip")
if (originalIP && clientIP !== originalIP) {
return { valid: false, reason: "IP address changed" }
}
return { valid: true }
}
// Force re-authentication
function requireReauthentication(reason) {
gs.getSession().invalidate()
gs.addErrorMessage(reason)
// Redirect to login
response.sendRedirect("/login.do")
}
MFA Implementation (ES5)
Check MFA Status
// Check if user has MFA enabled (ES5 ONLY!)
function hasMFAEnabled(userSysId) {
var user = new GlideRecord("sys_user")
if (!user.get(userSysId)) {
return false
}
// Check MFA settings
var mfa = new GlideRecord("sys_user_mfa")
mfa.addQuery("user", userSysId)
mfa.addQuery("active", true)
mfa.query()
return mfa.hasNext()
}
// Enforce MFA for sensitive operations
function requireMFA(operation) {
var userId = gs.getUserID()
if (!hasMFAEnabled(userId)) {
gs.addErrorMessage("MFA required for " + operation)
return false
}
// Check if MFA verified this session
var session = gs.getSession()
var mfaVerified = session.getValue("mfa_verified")
if (mfaVerified !== "true") {
// Trigger MFA challenge
gs.eventQueue("user.mfa.challenge", null, userId, operation)
return false
}
return true
}
Input Validation (ES5)
XSS Prevention
// Sanitize user input (ES5 ONLY!)
function sanitizeInput(input) {
if (!input) return ""
// Encode HTML entities
var sanitized = input
.toString()
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'")
return sanitized
}
// Validate and sanitize before storage
function validateUserInput(tableName, fieldName, value) {
// Check field type
var dict = new GlideRecord("sys_dictionary")
dict.addQuery("name", tableName)
dict.addQuery("element", fieldName)
dict.query()
if (!dict.next()) {
return { valid: false, error: "Field not found" }
}
var fieldType = dict.getValue("internal_type")
// Validate based on type
if (fieldType === "string" || fieldType === "html") {
// Check for script injection
if (/<script/i.test(value)) {
return { valid: false, error: "Script tags not allowed" }
}
// Check for event handlers
if (/on\w+\s*=/i.test(value)) {
return { valid: false, error: "Event handlers not allowed" }
}
}
return { valid: true, sanitized: sanitizeInput(value) }
}
SQL Injection Prevention
// Safe query building (ES5 ONLY!)
// NEVER concatenate user input directly into queries
// BAD - Vulnerable to injection
// gr.addEncodedQuery('name=' + userInput);
// GOOD - Use parameterized queries
function safeQuery(tableName, fieldName, value) {
var gr = new GlideRecord(tableName)
// GlideRecord methods handle escaping
gr.addQuery(fieldName, value)
gr.query()
return gr
}
// For encoded queries, validate input
function safeEncodedQuery(tableName, userQuery) {
// Whitelist allowed fields
var allowedFields = ["number", "short_description", "state", "priority"]
// Parse and validate query
var parts = userQuery.split("^")
var safeQuery = []
for (var i = 0; i < parts.length; i++) {
var part = parts[i]
var match = part.match(/^(\w+)(=|!=|LIKE|CONTAINS)(.+)$/)
if (match) {
var field = match[1]
if (allowedFields.indexOf(field) !== -1) {
safeQuery.push(part)
}
}
}
var gr = new GlideRecord(tableName)
if (safeQuery.length > 0) {
gr.addEncodedQuery(safeQuery.join("^"))
}
gr.query()
return gr
}
Security Properties (ES5)
Key Security Settings
// Get security-related properties (ES5 ONLY!)
function getSecuritySettings() {
return {
// Session settings
session_timeout: gs.getProperty("glide.security.session.timeout"),
session_cookie_secure: gs.getProperty("glide.security.session.cookie.secure"),
// Password settings
password_min_length: gs.getProperty("glide.security.password.min_length"),
password_history: gs.getProperty("glide.security.password.history"),
password_expiry: gs.getProperty("glide.security.password.expiry"),
// Login settings
max_failed_logins: gs.getProperty("glide.security.password.max_failed_logins"),
lockout_duration: gs.getProperty("glide.security.password.lockout_duration"),
// General security
csrf_protection: gs.getProperty("glide.security.csrf.strict_validation"),
xss_protection: gs.getProperty("glide.security.xss.strict"),
}
}
// Update security property
function setSecurityProperty(name, value, requireRestart) {
gs.setProperty(name, value)
if (requireRestart) {
gs.warn("Security property changed: " + name + ". Restart may be required.")
}
// Log security change
gs.eventQueue("security.property.changed", null, name, value)
}
Security Auditing (ES5)
Log Security Events
// Log security event (ES5 ONLY!)
function logSecurityEvent(eventType, details) {
var log = new GlideRecord("syslog")
log.initialize()
log.setValue("level", "warning")
log.setValue("source", "Security")
log.setValue("message", eventType + ": " + JSON.stringify(details))
log.insert()
// Also queue for security monitoring
gs.eventQueue("security.event", null, eventType, JSON.stringify(details))
}
// Track failed login attempts
function trackFailedLogin(username, ipAddress) {
logSecurityEvent("failed_login", {
username: username,
ip: ipAddress,
timestamp: new GlideDateTime().getDisplayValue(),
})
// Check for brute force
var recentFailures = countRecentFailures(username, 5) // Last 5 minutes
var maxFailures = parseInt(gs.getProperty("glide.security.password.max_failed_logins", "5"), 10)
if (recentFailures >= maxFailures) {
lockAccount(username)
logSecurityEvent("account_locked", {
username: username,
reason: "Too many failed login attempts",
failures: recentFailures,
})
}
}
Security Health Check
// Check instance security health (ES5 ONLY!)
function securityHealthCheck() {
var issues = []
// Check for default admin password
var admin = new GlideRecord("sys_user")
if (admin.get("user_name", "admin")) {
if (admin.getValue("password") === gs.getProperty("glide.security.default.admin.password")) {
issues.push({ severity: "critical", issue: "Default admin password not changed" })
}
}
// Check session timeout
var timeout = parseInt(gs.getProperty("glide.security.session.timeout", "0"), 10)
if (timeout === 0 || timeout > 60) {
issues.push({ severity: "high", issue: "Session timeout too long or disabled" })
}
// Check password policy
var minLength = parseInt(gs.getProperty("glide.security.password.min_length", "0"), 10)
if (minLength < 12) {
issues.push({ severity: "medium", issue: "Password minimum length less than 12" })
}
// Check HTTPS enforcement
if (gs.getProperty("glide.security.session.cookie.secure") !== "true") {
issues.push({ severity: "high", issue: "Secure cookies not enforced" })
}
return {
healthy: issues.length === 0,
issues: issues,
}
}
MCP Tool Integration
Available Tools
| Tool | Purpose |
|---|---|
snow_property_get |
Check security properties |
snow_execute_script_with_output |
Test security scripts |
snow_review_access_control |
Review ACLs |
Example Workflow
// 1. Check security properties
await snow_property_get({
name: "glide.security.session.timeout",
})
// 2. Run security health check
await snow_execute_script_with_output({
script: `
var health = securityHealthCheck();
gs.info(JSON.stringify(health));
`,
})
// 3. Review access controls
await snow_query_table({
table: "sys_security_acl",
query: "active=true^admin_overrides=true",
fields: "name,operation,type,script",
})
Best Practices
- Strong Passwords - Enforce complexity
- MFA - Enable for privileged users
- Session Timeout - 15-30 minutes
- Input Validation - Sanitize everything
- Least Privilege - Minimal roles
- Audit Logging - Log security events
- Regular Reviews - Security assessments
- ES5 Only - No modern JavaScript syntax
Weekly Installs
51
Repository
groeimetai/snow-flowGitHub Stars
53
First Seen
Jan 22, 2026
Security Audits
Installed on
claude-code47
opencode46
github-copilot46
codex46
gemini-cli46
cursor45