{
  "standard": "OARS",
  "name": "Open App Risk Standard",
  "specVersion": "0.1.0-draft",
  "status": "Draft",
  "license": "CC-BY-4.0",
  "homepage": "https://citadel.ms",
  "repository": "https://github.com/citadelms/OARS",
  "authors": [
    {
      "name": "Khurram Chaudhary",
      "role": "Founding Author"
    }
  ],
  "attribution": "Derived from the Graph Consent Risk Framework by Khurram Chaudhary. Licensed CC BY 4.0 — attribution required.",
  "generatedAt": "2026-07-26T01:12:26.340Z",
  "modelVersion": "0.1.0-draft",
  "application": {
    "groupBy": "dimension",
    "dimensions": [
      {
        "id": "application-profile-exposure",
        "name": "Application Profile & Exposure",
        "weight": 25,
        "maxScore": 15
      },
      {
        "id": "permissions-privilege-impact",
        "name": "Permissions & Privilege Impact",
        "weight": 50,
        "maxScore": 20
      },
      {
        "id": "governance-lifecycle",
        "name": "Governance & Lifecycle",
        "weight": 25,
        "maxScore": 16
      }
    ],
    "domains": [
      {
        "id": "app-trust-exposure",
        "name": "App Trust & Exposure",
        "description": "How much inherent trust the app deserves based on its source, maturity, tenant breadth, impersonation capability, and redirect-URI hygiene."
      },
      {
        "id": "auth-credential-hygiene",
        "name": "Authentication & Credential Hygiene",
        "description": "Strength of secrets or certificates, OAuth flow, managed-identity usage, and any privileged roles the app already holds."
      },
      {
        "id": "compliance-governance",
        "name": "Compliance & Governance",
        "description": "Whether the app has assigned ownership, serves an active business purpose, and exercises the access it has been granted."
      }
    ],
    "factors": [
      {
        "id": "applicationSource",
        "name": "Application Source",
        "domain": "app-trust-exposure",
        "dimension": "application-profile-exposure",
        "description": "Indicates who created the app: internal (trusted), external verified (moderate trust), or external unverified (high risk).",
        "signal": "application.publisherDomain, servicePrincipal.appOwnerOrganizationId",
        "maxScore": 3,
        "values": [
          {
            "id": "internal",
            "label": "Internal / approved tenant",
            "score": 0
          },
          {
            "id": "external-verified",
            "label": "External – verified",
            "score": 2
          },
          {
            "id": "external-unverified",
            "label": "External – unverified",
            "score": 3
          }
        ]
      },
      {
        "id": "tenantBreadth",
        "name": "Tenant Breadth",
        "domain": "app-trust-exposure",
        "dimension": "application-profile-exposure",
        "description": "Whether the app is single-tenant (low exposure) or multi-tenant (broad access). Multi-tenant apps increase exposure to external misuse.",
        "signal": "application.signInAudience",
        "maxScore": 2,
        "values": [
          {
            "id": "single-tenant",
            "label": "Single-tenant",
            "score": 0
          },
          {
            "id": "multi-tenant",
            "label": "Multi-tenant",
            "score": 2
          }
        ]
      },
      {
        "id": "comboPermissions",
        "name": "Combo Permissions",
        "domain": "app-trust-exposure",
        "dimension": "permissions-privilege-impact",
        "description": "Risk amplification when two or more permissions combine in a way that increases abuse potential. Evaluated against data/model/combos.yaml.",
        "signal": "oAuth2PermissionGrant, appRoleAssignment",
        "maxScore": 5,
        "values": [
          {
            "id": "none",
            "label": "No risky combination detected",
            "score": 0
          },
          {
            "id": "single-pairing",
            "label": "One known risky pairing present",
            "score": 2
          },
          {
            "id": "multiple",
            "label": "Multiple risky combinations",
            "score": 4
          },
          {
            "id": "escalation-pattern",
            "label": "Sensitive escalation pattern detected",
            "score": 5
          }
        ]
      },
      {
        "id": "appMaturity",
        "name": "App Maturity",
        "domain": "app-trust-exposure",
        "dimension": "governance-lifecycle",
        "description": "How long the app has been in the tenant. New apps may be malicious or unvetted; older apps may have governance history.",
        "signal": "application.createdDateTime",
        "maxScore": 3,
        "values": [
          {
            "id": "over-12-months",
            "label": "Over 12 months",
            "score": 0
          },
          {
            "id": "3-to-12-months",
            "label": "3–12 months",
            "score": 1
          },
          {
            "id": "under-30-days",
            "label": "Under 30 days",
            "score": 2
          },
          {
            "id": "under-7-days",
            "label": "Under 7 days",
            "score": 3
          }
        ]
      },
      {
        "id": "redirectUriMethod",
        "name": "Redirect URI Method",
        "domain": "app-trust-exposure",
        "dimension": "application-profile-exposure",
        "description": "How the app receives tokens. Impacts token security and exposure.",
        "signal": "application.web.redirectUris, application.publicClient.redirectUris",
        "maxScore": 3,
        "values": [
          {
            "id": "microsoft-uri",
            "label": "Microsoft URI",
            "score": 0
          },
          {
            "id": "loopback-or-mobile",
            "label": "Loopback / mobile URI",
            "score": 0
          },
          {
            "id": "static-https",
            "label": "Static HTTPS web URI",
            "score": 1
          },
          {
            "id": "custom-uri",
            "label": "Custom URI scheme",
            "score": 2
          },
          {
            "id": "wildcard-uri",
            "label": "Wildcard URI",
            "score": 3
          }
        ]
      },
      {
        "id": "redirectUriOwnership",
        "name": "Redirect URI Ownership",
        "domain": "app-trust-exposure",
        "dimension": "application-profile-exposure",
        "description": "Who owns the domain receiving tokens. Untrusted or wildcard domains increase risk of token hijacking.",
        "signal": "application.web.redirectUris, domain verification records",
        "maxScore": 3,
        "values": [
          {
            "id": "verified-https",
            "label": "Verified HTTPS (owned domain)",
            "score": 0
          },
          {
            "id": "third-party-verified",
            "label": "Third-party verified",
            "score": 1
          },
          {
            "id": "unverified",
            "label": "Unverified domain",
            "score": 2
          },
          {
            "id": "http-or-wildcard",
            "label": "HTTP or wildcard domain",
            "score": 3
          }
        ]
      },
      {
        "id": "userAssignmentEnforcement",
        "name": "User Assignment Enforcement",
        "domain": "app-trust-exposure",
        "dimension": "permissions-privilege-impact",
        "description": "Whether users or groups are explicitly assigned to the app. Lack of assignment control can result in broad unintended access.",
        "signal": "servicePrincipal.appRoleAssignmentRequired",
        "maxScore": 3,
        "values": [
          {
            "id": "assignment-required",
            "label": "Assignment required",
            "score": 0
          },
          {
            "id": "open-to-all",
            "label": "Open to all users",
            "score": 2
          },
          {
            "id": "open-with-guests",
            "label": "Open to all users including guests",
            "score": 3
          }
        ]
      },
      {
        "id": "guestAccess",
        "name": "Guest Access",
        "domain": "app-trust-exposure",
        "dimension": "governance-lifecycle",
        "description": "Whether B2B or federated users can access the app. Increases external exposure and weakens control.",
        "signal": "appRoleAssignedTo, external collaboration settings",
        "maxScore": 3,
        "values": [
          {
            "id": "guests-blocked",
            "label": "Guests blocked",
            "score": 0
          },
          {
            "id": "limited-guest",
            "label": "Limited guest access",
            "score": 1
          },
          {
            "id": "guests-allowed",
            "label": "Guests allowed",
            "score": 2
          },
          {
            "id": "guests-broad",
            "label": "Guests allowed broadly",
            "score": 3
          }
        ]
      },
      {
        "id": "publisherVerification",
        "name": "Publisher Verification",
        "domain": "app-trust-exposure",
        "dimension": "application-profile-exposure",
        "description": "Verified publishers are Microsoft-trusted; unverified may be fraudulent.",
        "signal": "application.verifiedPublisher",
        "maxScore": 2,
        "values": [
          {
            "id": "verified",
            "label": "Verified publisher",
            "score": 0
          },
          {
            "id": "unverified",
            "label": "Unverified publisher",
            "score": 2
          }
        ]
      },
      {
        "id": "m365Certification",
        "name": "M365 Certification",
        "domain": "app-trust-exposure",
        "dimension": "governance-lifecycle",
        "description": "Apps with M365 certification meet Microsoft security and compliance standards, reducing vetting burden.",
        "signal": "servicePrincipal.certification",
        "maxScore": 1,
        "values": [
          {
            "id": "certified",
            "label": "Microsoft 365 certified",
            "score": 0
          },
          {
            "id": "not-certified",
            "label": "Not certified",
            "score": 1
          }
        ]
      },
      {
        "id": "oauthClientClass",
        "name": "OAuth Client Class",
        "domain": "auth-credential-hygiene",
        "dimension": "permissions-privilege-impact",
        "description": "Whether the app can store secrets (confidential) or not (public). Public apps pose greater exposure risk.",
        "signal": "application.isFallbackPublicClient, application.publicClient",
        "maxScore": 3,
        "values": [
          {
            "id": "confidential-cert",
            "label": "Confidential client with certificate",
            "score": 0
          },
          {
            "id": "confidential-secret",
            "label": "Confidential client with secret",
            "score": 2
          },
          {
            "id": "public-client",
            "label": "Public client",
            "score": 3
          }
        ]
      },
      {
        "id": "oauthFlowSecurity",
        "name": "OAuth Flow Security",
        "domain": "auth-credential-hygiene",
        "dimension": "permissions-privilege-impact",
        "description": "OAuth grant type security. Some flows (ROPC, implicit) are deprecated or insecure.",
        "signal": "application.web.implicitGrantSettings, sign-in log authentication protocol",
        "maxScore": 3,
        "values": [
          {
            "id": "auth-code-pkce",
            "label": "Authorization code + PKCE",
            "score": 0
          },
          {
            "id": "obo-or-device-code",
            "label": "On-behalf-of / device code",
            "score": 1
          },
          {
            "id": "client-credentials",
            "label": "Client credentials",
            "score": 2
          },
          {
            "id": "ropc-or-implicit",
            "label": "ROPC / implicit",
            "score": 3
          }
        ]
      },
      {
        "id": "managedIdentityAdoption",
        "name": "Managed Identity Adoption",
        "domain": "auth-credential-hygiene",
        "dimension": "application-profile-exposure",
        "description": "Apps using managed identity avoid credential leakage and rotation risk.",
        "signal": "servicePrincipal.servicePrincipalType",
        "maxScore": 2,
        "values": [
          {
            "id": "managed-identity-only",
            "label": "Uses only managed identity",
            "score": 0
          },
          {
            "id": "certificate-only",
            "label": "Certificate only",
            "score": 1
          },
          {
            "id": "secret-or-mixed",
            "label": "Client secret or mixed",
            "score": 2
          }
        ]
      },
      {
        "id": "credentialStrength",
        "name": "Credential Strength",
        "domain": "auth-credential-hygiene",
        "dimension": "permissions-privilege-impact",
        "description": "How the app authenticates. Secrets are vulnerable unless rotated and stored securely.",
        "signal": "application.keyCredentials, application.passwordCredentials",
        "maxScore": 3,
        "values": [
          {
            "id": "managed-identity-or-cert",
            "label": "Managed identity or certificate",
            "score": 0
          },
          {
            "id": "secret-rotated",
            "label": "Secret",
            "rotated": null,
            "score": 1
          },
          {
            "id": "secret-static",
            "label": "Secret",
            "static": null,
            "score": 2
          },
          {
            "id": "secret-expired",
            "label": "Secret",
            "expired": null,
            "score": 3
          }
        ]
      },
      {
        "id": "ownershipAssignment",
        "name": "Ownership Assignment",
        "domain": "compliance-governance",
        "dimension": "governance-lifecycle",
        "description": "Apps with multiple owners are easier to govern. Orphaned apps increase lifecycle and accountability risks.",
        "signal": "application.owners",
        "maxScore": 3,
        "values": [
          {
            "id": "two-or-more-fte",
            "label": "Two or more full-time employee owners",
            "score": 0
          },
          {
            "id": "single-owner",
            "label": "Single owner",
            "score": 1
          },
          {
            "id": "no-active-owners",
            "label": "High-tier or above with no active owners",
            "score": 2
          },
          {
            "id": "guest-only-or-none",
            "label": "Guest-only owners or no owners",
            "score": 3
          }
        ]
      },
      {
        "id": "overPrivilegedAccess",
        "name": "Over-Privileged Access",
        "domain": "compliance-governance",
        "dimension": "permissions-privilege-impact",
        "description": "Apps granted permissions they do not use. Unused high-privilege scopes increase attack surface and violate least privilege. Measured over 90 days.",
        "signal": "Microsoft Graph activity logs, permission usage telemetry",
        "maxScore": 3,
        "values": [
          {
            "id": "at-least-90-percent-used",
            "label": "90% or more of permissions used",
            "score": 0
          },
          {
            "id": "50-to-90-percent-used",
            "label": "50–90% used",
            "score": 1
          },
          {
            "id": "under-50-percent-used",
            "label": "Under 50% used",
            "score": 2
          },
          {
            "id": "none-used-or-unused-high-privilege",
            "label": "None used",
            "or unused high-privilege scopes": null,
            "score": 3
          }
        ]
      },
      {
        "id": "businessNecessity",
        "name": "Business Necessity",
        "domain": "compliance-governance",
        "dimension": "governance-lifecycle",
        "description": "Whether the app serves an active business purpose. Obsolete or redundant apps expand attack surface even when secure.",
        "signal": "application catalogue, service ownership records",
        "maxScore": 3,
        "values": [
          {
            "id": "core-platform",
            "label": "Core platform or enterprise-critical app",
            "score": 0
          },
          {
            "id": "business-critical-lob",
            "label": "Business-critical LOB app with regular use",
            "score": 1
          },
          {
            "id": "low-usage-or-duplicate",
            "label": "Low usage",
            "or duplicates an existing app": null,
            "score": 2
          },
          {
            "id": "dormant-or-obsolete",
            "label": "Dormant",
            "unused": null,
            "or labelled obsolete/test": null,
            "score": 3
          }
        ]
      },
      {
        "id": "appActivity",
        "name": "App Activity",
        "domain": "compliance-governance",
        "dimension": "governance-lifecycle",
        "description": "Whether the app is actively used. Unused apps may indicate stale configuration, governance gaps, or abandoned access.",
        "signal": "servicePrincipalSignInActivity.lastSignInDateTime",
        "maxScore": 3,
        "values": [
          {
            "id": "active-30-days",
            "label": "Active in last 30 days",
            "score": 0
          },
          {
            "id": "used-31-90-days",
            "label": "Used within 31–90 days",
            "score": 1
          },
          {
            "id": "used-91-180-days",
            "label": "Used within 91–180 days",
            "score": 2
          },
          {
            "id": "inactive-180-days",
            "label": "No usage in past 180 days or ever",
            "score": 3
          }
        ]
      }
    ]
  },
  "permission": {
    "groupBy": "domain",
    "domains": [
      {
        "id": "identity-privilege-escalation",
        "name": "Identity & Privilege Escalation",
        "weight": 40,
        "maxScore": 140,
        "description": "Scopes that let an app impersonate users, assign roles, or elevate privileges."
      },
      {
        "id": "access-surface-blast-radius",
        "name": "Access Surface & Blast Radius",
        "weight": 30,
        "maxScore": 165,
        "description": "How wide and deep a permission reaches once granted."
      },
      {
        "id": "data-sensitivity-leakage",
        "name": "Data Sensitivity & Leakage",
        "weight": 25,
        "maxScore": 120,
        "description": "The value of the data reachable and the ease with which it can leave."
      },
      {
        "id": "compliance-trust-heuristics",
        "name": "Compliance & Trust Heuristics",
        "weight": 5,
        "maxScore": 40,
        "description": "Confidence-reducing indicators that signal audit gaps."
      }
    ],
    "factors": [
      {
        "id": "privilegeDepth",
        "name": "Privilege Depth",
        "domain": "identity-privilege-escalation",
        "description": "Admin depth reachable through the scope. Higher admin means higher takeover risk.",
        "maxScore": 40,
        "values": [
          {
            "id": "none",
            "label": "No administrative depth",
            "score": 0
          },
          {
            "id": "group-admin",
            "label": "Group administrator",
            "score": 20
          },
          {
            "id": "org-admin",
            "label": "Organisation administrator",
            "score": 30
          },
          {
            "id": "global-admin",
            "label": "Global administrator",
            "score": 40
          }
        ]
      },
      {
        "id": "privilegeElevation",
        "name": "Privilege Elevation",
        "domain": "identity-privilege-escalation",
        "description": "Ability to create, assign, or remove identities or roles. Enables lateral movement.",
        "maxScore": 40,
        "values": [
          {
            "id": "none",
            "label": "None",
            "score": 0
          },
          {
            "id": "user-group-edits",
            "label": "User or group edits",
            "score": 30
          },
          {
            "id": "role-assignment",
            "label": "Role assignment",
            "score": 40
          }
        ]
      },
      {
        "id": "impersonationCapability",
        "name": "Impersonation Capability",
        "domain": "identity-privilege-escalation",
        "description": "App can act as another user, obscuring audit trails.",
        "maxScore": 40,
        "values": [
          {
            "id": "none",
            "label": "None",
            "score": 0
          },
          {
            "id": "delegated-send-as",
            "label": "Delegated send-as",
            "score": 15
          },
          {
            "id": "app-wide",
            "label": "App-wide impersonation",
            "score": 30
          },
          {
            "id": "directory-wide",
            "label": "Directory-wide impersonation",
            "score": 40
          }
        ]
      },
      {
        "id": "appRoleBundles",
        "name": "App Role Bundles",
        "domain": "identity-privilege-escalation",
        "description": "Broad directory roles bundle many hidden scopes, hindering audit.",
        "maxScore": 20,
        "values": [
          {
            "id": "none",
            "label": "No bundle",
            "score": 0
          },
          {
            "id": "limited-role",
            "label": "Limited role",
            "score": 10
          },
          {
            "id": "broad-admin-role",
            "label": "Broad admin role",
            "score": 20
          }
        ]
      },
      {
        "id": "permissionType",
        "name": "Permission Type",
        "domain": "access-surface-blast-radius",
        "description": "Delegated (acts with a signed-in user) vs. application (daemon, no user oversight).",
        "maxScore": 25,
        "values": [
          {
            "id": "delegated",
            "label": "Delegated",
            "score": 10
          },
          {
            "id": "application",
            "label": "Application",
            "score": 25
          }
        ]
      },
      {
        "id": "permissionOperation",
        "name": "Permission Operation",
        "domain": "access-surface-blast-radius",
        "description": "The type of action enabled. Write and destructive operations carry higher impact than read-only access.",
        "maxScore": 30,
        "values": [
          {
            "id": "read",
            "label": "Read",
            "score": 0
          },
          {
            "id": "write",
            "label": "Write",
            "score": 20
          },
          {
            "id": "delete",
            "label": "Delete",
            "score": 20
          },
          {
            "id": "send",
            "label": "Send",
            "score": 30
          },
          {
            "id": "manage",
            "label": "Manage",
            "score": 30
          }
        ]
      },
      {
        "id": "permissionBreadth",
        "name": "Permission Breadth",
        "domain": "access-surface-blast-radius",
        "description": "Access breadth. Wider permissions amplify blast radius. Reduced when granted through Resource-Specific Consent.",
        "rscAdjusted": true,
        "maxScore": 40,
        "values": [
          {
            "id": "self",
            "label": "Self",
            "score": 0,
            "rscScore": 0
          },
          {
            "id": "single-user",
            "label": "Single user or object",
            "score": 10,
            "rscScore": 5
          },
          {
            "id": "group-org",
            "label": "Group or organisation",
            "score": 20,
            "rscScore": 10
          },
          {
            "id": "tenant",
            "label": "Tenant-wide",
            "score": 40,
            "rscScore": 20
          }
        ]
      },
      {
        "id": "accessPolicyModification",
        "name": "Access Policy Modification",
        "domain": "access-surface-blast-radius",
        "description": "Ability to change conditional access policies, group membership, or roles. Highest privilege vector.",
        "maxScore": 50,
        "values": [
          {
            "id": "none",
            "label": "None",
            "score": 0
          },
          {
            "id": "group-role-edits",
            "label": "Group or role edits",
            "score": 20
          },
          {
            "id": "policy-changes",
            "label": "Conditional access or policy changes",
            "score": 40
          },
          {
            "id": "full-policy-control",
            "label": "Full access-policy control",
            "score": 50
          }
        ]
      },
      {
        "id": "userBreadth",
        "name": "User Breadth",
        "domain": "access-surface-blast-radius",
        "description": "Number of identities affected. Larger reach means faster damage.",
        "maxScore": 20,
        "values": [
          {
            "id": "one",
            "label": "One user",
            "score": 0
          },
          {
            "id": "group",
            "label": "A group of users",
            "score": 10
          },
          {
            "id": "all-users",
            "label": "All users",
            "score": 20
          }
        ]
      },
      {
        "id": "dataClassification",
        "name": "Data Classification",
        "domain": "data-sensitivity-leakage",
        "description": "Sensitivity of the data reachable. Drives legal and audit risk.",
        "maxScore": 40,
        "values": [
          {
            "id": "none",
            "label": "No data access",
            "score": 0
          },
          {
            "id": "internal",
            "label": "Internal",
            "score": 15
          },
          {
            "id": "confidential",
            "label": "Confidential",
            "score": 25
          },
          {
            "id": "pii-regulated",
            "label": "PII or regulated",
            "score": 40
          }
        ]
      },
      {
        "id": "exfiltrationPotential",
        "name": "Exfiltration Potential",
        "domain": "data-sensitivity-leakage",
        "description": "Ability to read, sync, or export data at scale. Reduced when granted through Resource-Specific Consent.",
        "rscAdjusted": true,
        "maxScore": 40,
        "values": [
          {
            "id": "none",
            "label": "No data access or read-self only",
            "score": 0,
            "rscScore": 0
          },
          {
            "id": "basic-read",
            "label": "Basic read",
            "score": 15,
            "rscScore": 10
          },
          {
            "id": "collaborative-write",
            "label": "Write to collaborative content",
            "score": 25,
            "rscScore": 18
          },
          {
            "id": "send-as-delivery",
            "label": "Send-as or message delivery",
            "score": 35,
            "rscScore": 25
          },
          {
            "id": "sync-bulk",
            "label": "Sync",
            "migration": null,
            "or bulk read-write": null,
            "score": 35,
            "rscScore": 20
          },
          {
            "id": "tenant-export",
            "label": "Export or tenant-wide exfiltration",
            "score": 40,
            "rscScore": 25
          }
        ]
      },
      {
        "id": "tokenPersistence",
        "name": "Token Persistence",
        "domain": "data-sensitivity-leakage",
        "description": "Long-lived or refresh tokens persist after compromise.",
        "maxScore": 40,
        "values": [
          {
            "id": "short-lived",
            "label": "Short-lived",
            "score": 10
          },
          {
            "id": "refresh",
            "label": "Refresh token",
            "score": 25
          },
          {
            "id": "offline",
            "label": "Offline or long-lived",
            "score": 40
          }
        ]
      },
      {
        "id": "legacyApiRisk",
        "name": "Legacy API Risk",
        "domain": "compliance-trust-heuristics",
        "description": "Reliance on deprecated or unsupported APIs, which lack modern security controls.",
        "maxScore": 40,
        "values": [
          {
            "id": "modern",
            "label": "Modern API",
            "score": 0
          },
          {
            "id": "deprecated",
            "label": "Deprecated API",
            "score": 30
          },
          {
            "id": "unsupported",
            "label": "Unsupported API",
            "score": 40
          }
        ]
      }
    ]
  },
  "tiers": [
    {
      "id": "low",
      "name": "Low",
      "min": 0,
      "max": 29,
      "colour": "#2E7D32"
    },
    {
      "id": "medium",
      "name": "Medium",
      "min": 30,
      "max": 59,
      "colour": "#ED6C02"
    },
    {
      "id": "high",
      "name": "High",
      "min": 60,
      "max": 79,
      "colour": "#D84315"
    },
    {
      "id": "critical",
      "name": "Critical",
      "min": 80,
      "max": 100,
      "colour": "#B71C1C"
    }
  ],
  "combination": "highest-risk-wins",
  "enforcement": {
    "normative": false,
    "outcomes": [
      {
        "id": "auto-approve",
        "name": "Auto-approve",
        "description": "Granted without human intervention; decision and inputs logged for audit."
      },
      {
        "id": "analyst-review",
        "name": "Analyst review",
        "description": "A security analyst reviews the scored request against documented criteria."
      },
      {
        "id": "exception-review",
        "name": "Exception review",
        "description": "Formal exception process with named accountable ownership, documented business justification, compensating-control assessment, and expiry date."
      }
    ],
    "matrix": {
      "low": {
        "low": "auto-approve",
        "medium": "analyst-review",
        "high": "analyst-review",
        "critical": "exception-review"
      },
      "medium": {
        "low": "analyst-review",
        "medium": "analyst-review",
        "high": "exception-review",
        "critical": "exception-review"
      },
      "high": {
        "low": "analyst-review",
        "medium": "exception-review",
        "high": "exception-review",
        "critical": "exception-review"
      },
      "critical": {
        "low": "exception-review",
        "medium": "exception-review",
        "high": "exception-review",
        "critical": "exception-review"
      }
    }
  },
  "scoring": {
    "method": "normalize-weight-aggregate",
    "description": "Within each grouping, sum the raw factor scores and divide by the grouping maximum; multiply by the grouping weight; sum the weighted contributions for a 0-100 composite.",
    "rounding": "half-up to one decimal place",
    "tierMatching": "Match the highest tier whose lower bound the score reaches, so fractional scores between published bounds resolve downward."
  }
}
