mcpField
A
acl-security

acl-security

github.com/serac-labsセキュリティ7761 回インストール9 週間前に更新
リポジトリ

acl-security について

ServiceNow の開発者や管理者向けに、ACL の適用規則、ロール確認、一般的な設定パターンを解説するガイドです。継承テーブルやワイルドカードの名前、診断の限界、安全な ACL 作成手順を扱います。

主な機能

  1. 01レコード/フィールド ACL の整理
  2. 02継承テーブルとワイルドカード規則の比較
  3. 03ルールごとのロール差分の説明
  4. 04ACL の作成と確認手順
  5. 05一般的なアクセス制御パターン

活用シーン

  1. 01レコードやフィールドのアクセス障害の調査
  2. 02ロールや所有権に基づくアクセス設計
  3. 03ACL 作成後のロール設定確認

MCP 対応

適用される ACL の収集に使う snow_acl_explain、ACL の枠を作成する snow_create_acl、監査付き偽装リンクを作る snow_impersonate_user など、ServiceNow の MCP ツールを説明します。判定を誤って報告する可能性がある snow_test_acl には依存しないよう注意しています。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/serac-labs/serac --skill acl-security

インストール後、Claude Code で/acl-securityを入力して読み込まれたか確認します。

詳細

ServiceNow の ACL セキュリティパターン

アクセス制御リスト(ACL)は、ServiceNow のセキュリティの基盤です。レコードの読み取り、書き込み、作成、削除を行えるユーザーを制御します。

適用される ACL

ACL は名前で適用され、その名前はリテラルです。つまり、sys_security_acl.name に格納されるのは incident.priority や * であり、グロブパターンではありません。別のテーブルを拡張するテーブルのあるフィールドには、次の 6 つの名前のそれぞれにルールを設定できます。

名前ルールが適用される対象
incident.priorityこのテーブルのこのフィールド
task.priority親テーブルのこのフィールド
*.priorityあらゆるテーブルにある、この名前のフィールド
incident.*このテーブルのあらゆるフィールド
task.*親テーブルのあらゆるフィールド
*.*あらゆるテーブルのあらゆるフィールド

これらに加えて、レコードレベルの名前もあります。incident、次に sys_db_object.super_class の階層にある各親テーブル、そして * です。

これらの形式のうち、ライブインスタンスで確認されているものが 2 つあります。レコードレベルの *(sn-roles.manifest.json の背後にある ACL プローブは、これを通じて 189 個のツールプリミティブを解決しました)と、table.field(snow_analyze_form は nameSTARTSWITH<table>. を使ってフィールド ACL を読み取ります)です。上記の *.priority、incident.*、*.* に関する説明は同じ命名方式に基づいていますが、ServiceNow 自身のドキュメントを出典としていません。文書化された動作ではなく、確認する価値のある名前として扱ってください。snow_acl_explain はいずれにしてもすべてを確認し、使われていない名前は何も返しません。

これらすべてを確認してください。incident.priority だけを調べて止めてしまうと、デバッグ対象のまさにそのケースで、誤ったブロッカーを特定することになります。ルールが task またはワイルドカードに設定されている場合です。

snow_acl_explain は、1 回の呼び出しですべてを収集します。親テーブル名については sys_db_object.super_class をたどり、一致する各ルールの必須ロールを sys_security_acl_role の m2m から読み取り(sys_security_acl に roles 列はないため、そこを読み取るものはすべてロールなしと報告します)、それらをユーザーが sys_user_has_role で保持しているロールと比較します。ここにはすでに継承されたロールも含まれているため、自分で sys_user_role_contains を展開する必要はありません。

この差分は正しく読み取ってください。1 つのルールに設定されたロールは選択肢なので、ルールの roles_not_held は requires_roles の残りの部分であり、付与すべきロールの一覧ではありません。そのルールの user_holds が空でなければ、ロールチェックはすでに通過しており、ブロッカーは別の場所、つまり別の階層か、ツールが評価しなかった条件またはスクリプトにあります。

このツールが行わないのは、判定を下すことです。条件と高度なスクリプトは、プラットフォーム内で特定の 1 レコードに対して実行されます。REST Table API からそれらを評価する方法はないため、出力は「適用されるものと、ユーザーが保持しているもの」として読み取り、その後、ユーザーになりすまして確認してください。snow_test_acl は判定そのものを返すと称していますが、レコードを sys_script_execution に POST し、Table API のエコーをスクリプト出力のように報告するうえ、operation と user の両引数を無視します。使用しないでください。

ACL の種類

種類制御対象例
record行レベルのアクセスユーザーはこのインシデントを表示できますか?
fieldフィールドレベルのアクセスユーザーは assignment_group を表示できますか?
client_callable_script_includeScript Include へのアクセスユーザーはこの API を呼び出せますか?
ui_pageUI Page へのアクセスユーザーはこのページを表示できますか?
rest_endpointREST API へのアクセスユーザーはこのエンドポイントを呼び出せますか?

MCP 経由での ACL の作成

snow_create_acl は ACL のシェルのみを作成し、それ以外は何も行いません。name、operation、type、admin_overrides、active を受け取りますが、roles、condition、script の引数はありません。これらを渡しても黙って無視され、sys_security_acl に渡されることはありません。この段階のままの ACL にはロールも条件も設定されておらず、記述できるルールの中で最も制限が緩い状態です。他の 2 つの手順が完了するまでは、active: false に設定してください。

// 1. The shell — a record-level WRITE ACL on incident, created inactive.
snow_create_acl({
  name: "incident",
  operation: "write",
  type: "record",
  admin_overrides: true,
  active: false,
})

// 2. The roles. They live in the sys_security_acl_role m2m, one row per role, so
//    this is one call per role and each one needs that role's sys_id.
snow_query_table({ table: "sys_user_role", query: "nameINitil,incident_manager", fields: ["sys_id", "name"] })
snow_create_acl_role({ acl: "<acl sys_id>", role: "<itil sys_id>" })
snow_create_acl_role({ acl: "<acl sys_id>", role: "<incident_manager sys_id>" })

// 3. Condition and script are columns on the ACL record itself, with no argument
//    on snow_create_acl. Set them by updating the record, then activate it.
snow_record_manage({
  action: "update",
  table: "sys_security_acl",
  sys_id: "<acl sys_id>",
  data: { script: "answer = current.state < 6;", active: true },
})

1 つの ACL に設定した 2 つのロールは代替関係にあります。どちらか一方で、そのルールに基づくアクセスが許可されます。そのため、手順 2 では行を追加するたびにルールの範囲が広がり、狭まることはありません。

condition と script はどちらも sys_security_acl の実際のカラムです(snow_analyze_form で値を読み戻せます)。ACL フォームの Condition フィールドと Script フィールドが求める形式で、それぞれ記述してください。

フィールドレベルのルールも、name: "incident.priority" を指定する以外は同じ 3 つの手順です。

作業内容は、snow_acl_explain({ table: "incident", operation: "write", user: "<someone>" }) で確認してください。これは m2m からロールを読み戻します。最も省略されやすい手順です。

よくある ACL パターン

パターン 1:ロールベースのアクセス
// Condition: (empty - role check only)
// Roles: itil, incident_manager
// Script: (empty)

// Users with itil OR incident_manager role can access
パターン 2:所有権ベースのアクセス
// Condition:
current.caller_id == gs.getUserID() || current.assigned_to == gs.getUserID() || current.opened_by == gs.getUserID()

// User can access their own records
パターン 3:グループベースのアクセス
// Script:
;(function () {
  var userGroups = gs.getUser().getMyGroups()
  answer = userGroups.indexOf(current.assignment_group.toString()) >= 0
})()

// User can access records assigned to their groups
パターン 4:マネージャーチェーンによるアクセス
// 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
})()
パターン 5:時間ベースのアクセス
// 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
})()
パターン 6:データ分類
// 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

セキュリティのベストプラクティス

1. 最小権限の原則
// ❌ BAD - Too permissive
// Roles: (empty) - allows everyone

// ✅ GOOD - Explicit roles
// Roles: itil, incident_manager
2. デフォルトで拒否する
// Create a catch-all deny ACL at lowest priority
// Name: *
// Operation: read
// Condition: false
// This ensures anything not explicitly allowed is denied
3. 複雑なスクリプトを避ける
// ❌ 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)
4. ACL を十分にテストする
// Use "Impersonate User" to test ACLs as different users
// Check: Navigation, List views, Forms, Related lists
// Verify: Fields hidden, buttons disabled, records filtered

snow_impersonate_user を使用すると、監査対象のなりすまし用ディープリンクを生成できます(管理者のみ使用可能。~/.serac/audit/impersonations.jsonl に書き込みます)。ACL の失敗を診断する前に、snow_session_context を使用して、呼び出し元の現在のロールと更新セットを確認してください。

ACL のデバッグ

ACL デバッグを有効にする
// 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 のみ行レベルのセキュリティがないデータ分離の条件を追加する
DB を照会するスクリプトパフォーマンスの問題条件を使用するか、結果をキャッシュする
管理者としてのみテストする管理者は ACL を迂回する実際のエンドユーザーとしてテストする
REST API を忘れるAPI は UI の ACL を迂回する専用の REST ACL を作成する

コメント