重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
acl-security by groeimetai/snow-flow
npx skills add https://github.com/groeimetai/snow-flow --skill acl-security访问控制列表(ACLs)是 ServiceNow 安全性的基础。它们控制谁可以读取、写入、创建和删除记录。
ACL 按以下顺序评估(首次匹配即生效):
incident.assignment_group)* - 全局通配符(兜底)| 类型 | 控制范围 | 示例 |
|---|---|---|
| record | 行级访问 | 用户能看到此事件吗? |
| field | 字段级访问 | 用户能看到 assignment_group 吗? |
| client_callable_script_include |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 脚本包含访问 |
| 用户可以调用此 API 吗? |
| ui_page | UI 页面访问 | 用户可以查看此页面吗? |
| rest_endpoint | REST API 访问 | 用户可以调用此端点吗? |
// Table-level READ ACL
snow_create_acl({
name: "incident",
operation: "read",
admin_overrides: true,
active: true,
roles: ["itil", "incident_manager"],
condition: "current.active == true",
script: "",
})
// Field-level WRITE ACL
snow_create_acl({
name: "incident.priority",
operation: "write",
roles: ["incident_manager"],
condition: "",
script: "answer = current.state < 6;", // Only if not resolved
})
// Condition: (empty - role check only)
// Roles: itil, incident_manager
// Script: (empty)
// Users with itil OR incident_manager role can access
// Condition:
current.caller_id == gs.getUserID() || current.assigned_to == gs.getUserID() || current.opened_by == gs.getUserID()
// User can access their own records
// Script:
;(function () {
var userGroups = gs.getUser().getMyGroups()
answer = userGroups.indexOf(current.assignment_group.toString()) >= 0
})()
// User can access records assigned to their groups
// Script:
;(function () {
var callerManager = current.caller_id.manager
var currentUser = gs.getUserID()
// Check if current user is in caller's management chain
while (callerManager && !callerManager.nil()) {
if (callerManager.toString() == currentUser) {
answer = true
return
}
callerManager = callerManager.manager
}
answer = false
})()
// Script:
;(function () {
var now = new GlideDateTime()
var hour = parseInt(now.getLocalTime().getHourOfDayLocalTime())
// Only allow access during business hours (8 AM - 6 PM)
answer = hour >= 8 && hour < 18
})()
// Script:
;(function () {
var classification = current.u_data_classification.toString()
var userClearance = gs.getUser().getRecord().getValue("u_security_clearance")
var levels = { public: 0, internal: 1, confidential: 2, secret: 3 }
answer = levels[userClearance] >= levels[classification]
})()
// ACL: incident.u_ssn (Social Security Number)
// Operation: read
// Script:
answer = gs.hasRole("hr_admin")
// Only HR admins can see SSN field
// ACL: incident.short_description
// Operation: write
// Script:
answer = current.state < 6 // Can't edit after Resolved
// Prevent editing after resolution
// ACL: incident.u_internal_notes
// Operation: read
// Condition:
gs.hasRole("itil") || current.caller_id == gs.getUserID()
// ITIL users see all, callers see their own
// ❌ 错误 - 权限过大
// Roles: (empty) - allows everyone
// ✅ 正确 - 明确指定角色
// Roles: itil, incident_manager
// Create a catch-all deny ACL at lowest priority
// Name: *
// Operation: read
// Condition: false
// This ensures anything not explicitly allowed is denied
// ❌ 错误 - 复杂脚本 ACL(速度慢)
;(function () {
var gr = new GlideRecord("sys_user_grmember")
gr.addQuery("user", gs.getUserID())
gr.query()
while (gr.next()) {
// Complex logic...
}
})()
// ✅ 正确 - 尽可能使用条件
// Condition: gs.getUser().isMemberOf(current.assignment_group)
// Use "Impersonate User" to test ACLs as different users
// Check: Navigation, List views, Forms, Related lists
// Verify: Fields hidden, buttons disabled, records filtered
// In a background script or temporarily in your code:
gs.setProperty("glide.security.debug", "true")
gs.log("ACL Debug enabled")
// Check System Logs for ACL evaluation details
// Check if current user can read a record
var gr = new GlideRecord("incident")
gr.get("sys_id_here")
gs.info("Can Read: " + gr.canRead())
gs.info("Can Write: " + gr.canWrite())
gs.info("Can Delete: " + gr.canDelete())
// Check field-level
gs.info("Can read assignment_group: " + gr.assignment_group.canRead())
gs.info("Can write assignment_group: " + gr.assignment_group.canWrite())
| 错误 | 问题 | 解决方案 |
|---|---|---|
| 自定义表上没有 ACL | 任何人都可以访问 | 立即创建 ACL |
| 仅基于角色的 ACL | 没有行级安全性 | 为数据隔离添加条件 |
| 查询数据库的脚本 | 性能问题 | 使用条件或缓存结果 |
| 仅以管理员身份测试 | 管理员绕过 ACL | 以实际最终用户身份测试 |
| 忘记 REST API | API 绕过 UI ACL | 创建特定的 REST ACL |
每周安装数
52
仓库
GitHub 星标数
58
首次出现
2026年1月22日
安全审计
安装于
claude-code48
opencode46
github-copilot46
codex46
gemini-cli46
cursor45
Access Control Lists (ACLs) are the foundation of ServiceNow security. They control who can read, write, create, and delete records.
ACLs are evaluated in this order (first match wins):
incident.assignment_group)* - Global wildcard (catch-all)| Type | Controls | Example |
|---|---|---|
| record | Row-level access | Can user see this incident? |
| field | Field-level access | Can user see assignment_group? |
| client_callable_script_include | Script Include access | Can user call this API? |
| ui_page | UI Page access | Can user view this page? |
| rest_endpoint | REST API access | Can user call this endpoint? |
// Table-level READ ACL
snow_create_acl({
name: "incident",
operation: "read",
admin_overrides: true,
active: true,
roles: ["itil", "incident_manager"],
condition: "current.active == true",
script: "",
})
// Field-level WRITE ACL
snow_create_acl({
name: "incident.priority",
operation: "write",
roles: ["incident_manager"],
condition: "",
script: "answer = current.state < 6;", // Only if not resolved
})
// Condition: (empty - role check only)
// Roles: itil, incident_manager
// Script: (empty)
// Users with itil OR incident_manager role can access
// Condition:
current.caller_id == gs.getUserID() || current.assigned_to == gs.getUserID() || current.opened_by == gs.getUserID()
// User can access their own records
// Script:
;(function () {
var userGroups = gs.getUser().getMyGroups()
answer = userGroups.indexOf(current.assignment_group.toString()) >= 0
})()
// User can access records assigned to their groups
// Script:
;(function () {
var callerManager = current.caller_id.manager
var currentUser = gs.getUserID()
// Check if current user is in caller's management chain
while (callerManager && !callerManager.nil()) {
if (callerManager.toString() == currentUser) {
answer = true
return
}
callerManager = callerManager.manager
}
answer = false
})()
// Script:
;(function () {
var now = new GlideDateTime()
var hour = parseInt(now.getLocalTime().getHourOfDayLocalTime())
// Only allow access during business hours (8 AM - 6 PM)
answer = hour >= 8 && hour < 18
})()
// Script:
;(function () {
var classification = current.u_data_classification.toString()
var userClearance = gs.getUser().getRecord().getValue("u_security_clearance")
var levels = { public: 0, internal: 1, confidential: 2, secret: 3 }
answer = levels[userClearance] >= levels[classification]
})()
// ACL: incident.u_ssn (Social Security Number)
// Operation: read
// Script:
answer = gs.hasRole("hr_admin")
// Only HR admins can see SSN field
// ACL: incident.short_description
// Operation: write
// Script:
answer = current.state < 6 // Can't edit after Resolved
// Prevent editing after resolution
// ACL: incident.u_internal_notes
// Operation: read
// Condition:
gs.hasRole("itil") || current.caller_id == gs.getUserID()
// ITIL users see all, callers see their own
// ❌ BAD - Too permissive
// Roles: (empty) - allows everyone
// ✅ GOOD - Explicit roles
// Roles: itil, incident_manager
// Create a catch-all deny ACL at lowest priority
// Name: *
// Operation: read
// Condition: false
// This ensures anything not explicitly allowed is denied
// ❌ BAD - Complex script ACL (slow)
;(function () {
var gr = new GlideRecord("sys_user_grmember")
gr.addQuery("user", gs.getUserID())
gr.query()
while (gr.next()) {
// Complex logic...
}
})()
// ✅ GOOD - Use conditions when possible
// Condition: gs.getUser().isMemberOf(current.assignment_group)
// Use "Impersonate User" to test ACLs as different users
// Check: Navigation, List views, Forms, Related lists
// Verify: Fields hidden, buttons disabled, records filtered
// In a background script or temporarily in your code:
gs.setProperty("glide.security.debug", "true")
gs.log("ACL Debug enabled")
// Check System Logs for ACL evaluation details
// Check if current user can read a record
var gr = new GlideRecord("incident")
gr.get("sys_id_here")
gs.info("Can Read: " + gr.canRead())
gs.info("Can Write: " + gr.canWrite())
gs.info("Can Delete: " + gr.canDelete())
// Check field-level
gs.info("Can read assignment_group: " + gr.assignment_group.canRead())
gs.info("Can write assignment_group: " + gr.assignment_group.canWrite())
| Mistake | Problem | Solution |
|---|---|---|
| No ACLs on custom tables | Anyone can access | Create ACLs immediately |
| Only role-based ACLs | No row-level security | Add conditions for data segregation |
| Scripts that query DB | Performance issues | Use conditions or cache results |
| Testing only as admin | Admin bypasses ACLs | Test as actual end users |
| Forgetting REST APIs | APIs bypass UI ACLs | Create specific REST ACLs |
Weekly Installs
52
Repository
GitHub Stars
58
First Seen
Jan 22, 2026
Security Audits
Gen Agent Trust HubFailSocketPassSnykPass
Installed on
claude-code48
opencode46
github-copilot46
codex46
gemini-cli46
cursor45
Lark Mail CLI 使用指南:邮件管理、安全规则与自动化工作流
43,200 周安装