cosec-policy-author

star 43

Use when writing, validating, explaining, or debugging CoSec policy JSON, policy statements, ALLOW/DENY rules, action matchers, condition matchers, role-based authorization, tenant scoping, or rate limiting policies.

Ahoo-Wang By Ahoo-Wang schedule Updated 5/27/2026

name: cosec-policy-author description: "Use when writing, validating, explaining, or debugging CoSec policy JSON, policy statements, ALLOW/DENY rules, action matchers, condition matchers, role-based authorization, tenant scoping, or rate limiting policies."

CoSec Policy Authoring

This skill helps you write, validate, and explain CoSec security policy JSON files. CoSec uses an AWS IAM-like policy model where policies contain statements that define ALLOW or DENY rules matched against requests.

Policy JSON Structure

A policy file is a single JSON object placed in src/main/resources/cosec-policy/. The file naming convention is *-policy.json.

{
  "id": "unique-policy-id",
  "name": "Human-readable name",
  "category": "optional-category",
  "description": "What this policy does",
  "type": "global",
  "tenantId": "(platform)",
  "condition": { ... },
  "statements": [ ... ]
}

Fields

Field Required Description
id Yes Unique identifier for the policy
name Yes Human-readable display name
category No Logical grouping label
description No Detailed description
type Yes global (applies to all requests), system (system-level), or custom (user/role-specific)
tenantId Yes Tenant scope. Use (platform) for global/system policies
condition No Policy-level ConditionMatcher — if present and doesn't match, entire policy is skipped
statements Yes Array of Statement objects

Statement Structure

Each statement defines a single permission rule:

{
  "name": "StatementName",
  "effect": "allow",
  "action": "...",
  "condition": { ... }
}
Field Required Default Description
name No Descriptive name for the statement
effect No "allow" "allow" or "deny". DENY takes precedence over ALLOW
action Yes ActionMatcher definition (see below)
condition No ConditionMatcher definition (see below)

Evaluation Order

  1. Policy-level condition checked first — if it doesn't match, the entire policy returns IMPLICIT_DENY
  2. DENY statements evaluated first — any match returns EXPLICIT_DENY immediately
  3. ALLOW statements evaluated next — any match returns ALLOW
  4. Default: IMPLICIT_DENY

This means you should write DENY rules before ALLOW rules in the statements array for clarity, though the framework handles ordering internally.

Action Matchers

The action field defines which requests a statement applies to. There are several formats:

Simple string — path pattern

"action": "/api/users"

Matches the exact path. Supports Spring path patterns with wildcards and variables.

String with SpEL template

"action": "/user/#{principal.id}/*"

#{principal.id} is evaluated at match time against the current security context principal.

String with path variables

"action": "/user/{id}"

Matches path segments. Access the variable in conditions via request.path.var.id.

Array — multiple paths (OR logic)

"action": ["/auth/register", "/auth/login", "/auth/logout"]

Matches if ANY path in the array matches.

Wildcard

"action": "*"

Matches all requests. Use with conditions to restrict scope.

Object — path matcher with options

"action": {
  "path": {
    "method": "GET",
    "pattern": "/api/users/*",
    "options": {
      "caseSensitive": false,
      "separator": "/",
      "decodeAndParseSegments": false
    }
  }
}

The method field can be a single string or array: "method": ["GET", "POST"].

The pattern field can be a single string or array of patterns.

Object — all matcher with method filter

"action": {
  "all": {
    "method": "GET"
  }
}

Matches all GET requests regardless of path.

Object — composite matcher (OR logic across different matcher types)

"action": {
  "composite": [
    "/api/public/*",
    {
      "path": {
        "method": "POST",
        "pattern": "/api/webhook/*"
      }
    }
  ]
}

Condition Matchers

The condition field adds additional constraints beyond path matching. All condition types:

authenticated — user must be logged in

"condition": { "authenticated": {} }

inRole — user must have the specified role

"condition": { "inRole": { "value": "admin" } }

inTenant — request must be from the specified tenant

"condition": { "inTenant": { "value": "tenant-abc" } }

eq — exact value match

"condition": {
  "eq": {
    "part": "request.path.var.id",
    "value": "#{principal.id}"
  }
}

The part field is a path expression that extracts a value from the request or security context:

  • request.path.var.{name} — path variable
  • request.remoteIp — client IP
  • request.origin — request origin
  • request.method — HTTP method
  • request.attributes.{key} — request attributes (e.g., request.attributes.ipRegion)
  • context.principal.id — current user ID
  • context.principal.attributes.{key} — principal attributes

The value field supports SpEL templates like #{principal.id}.

contains — substring match

"condition": {
  "contains": {
    "part": "request.attributes.ipRegion",
    "value": "上海"
  }
}

startsWith / endsWith — prefix/suffix match

"condition": {
  "startsWith": {
    "part": "request.attributes.ipRegion",
    "value": "中国"
  }
}

in — value must be in a list

"condition": {
  "in": {
    "part": "context.principal.id",
    "value": ["adminId", "developerId"]
  }
}

regular — regex match

"condition": {
  "regular": {
    "negate": true,
    "part": "request.origin",
    "pattern": "^(http|https)://github.com"
  }
}

Set negate: true to invert the match (matches when regex does NOT match).

path — path pattern match on arbitrary values

"condition": {
  "path": {
    "part": "request.remoteIp",
    "pattern": "192.168.0.*",
    "options": {
      "caseSensitive": false,
      "separator": ".",
      "decodeAndParseSegments": false
    }
  }
}

bool — boolean logic (AND/OR)

"condition": {
  "bool": {
    "and": [
      { "authenticated": {} }
    ],
    "or": [
      { "in": { "part": "context.principal.id", "value": ["dev1"] } },
      { "path": { "part": "request.remoteIp", "pattern": "10.0.0.*" } }
    ]
  }
}

All items in and must match. At least one item in or must match. Both are optional — you can use just and, just or, or both.

spel / ognl — expression-based

"condition": {
  "spel": {
    "expression": "#principal.attributes['vip'] == 'true'"
  }
}

rateLimiter — rate limiting

"condition": {
  "rateLimiter": {
    "permitsPerSecond": 10
  }
}

groupedRateLimiter — grouped rate limiting

"condition": {
  "groupedRateLimiter": {
    "permitsPerSecond": 100,
    "groupKey": "context.principal.id"
  }
}

Common Patterns

Public endpoints (no auth required)

{
  "id": "public-api",
  "name": "Public API",
  "type": "global",
  "tenantId": "(platform)",
  "statements": [
    {
      "name": "PublicEndpoints",
      "action": ["/auth/login", "/auth/register", "/health"]
    }
  ]
}

Admin-only endpoints

{
  "id": "admin-api",
  "name": "Admin API",
  "type": "global",
  "tenantId": "(platform)",
  "statements": [
    {
      "name": "AdminOnly",
      "action": "/admin/**",
      "condition": { "inRole": { "value": "admin" } }
    }
  ]
}

User can only access their own resources

{
  "name": "OwnResourcesOnly",
  "action": "/api/users/{id}/**",
  "condition": {
    "eq": {
      "part": "request.path.var.id",
      "value": "#{principal.id}"
    }
  }
}

IP whitelist with deny

{
  "name": "BlockExternalIp",
  "effect": "deny",
  "action": "*",
  "condition": {
    "regular": {
      "negate": true,
      "part": "request.remoteIp",
      "pattern": "^(10\\.0\\.0\\.|192\\.168\\.)"
    }
  }
}

CORS origin restriction

{
  "name": "RestrictOrigin",
  "effect": "deny",
  "action": "*",
  "condition": {
    "regular": {
      "negate": true,
      "part": "request.origin",
      "pattern": "^https://(app\\.example\\.com|admin\\.example\\.com)"
    }
  }
}

Rate-limited authenticated API

{
  "name": "RateLimitedApi",
  "action": "/api/**",
  "condition": {
    "bool": {
      "and": [
        { "authenticated": {} },
        { "rateLimiter": { "permitsPerSecond": 10 } }
      ]
    }
  }
}

Health probe (for Kubernetes)

{
  "id": "(health-probe)",
  "name": "Health Probe",
  "type": "global",
  "tenantId": "(platform)",
  "statements": [
    {
      "name": "actuator",
      "action": [
        "/actuator/health",
        "/actuator/health/readiness",
        "/actuator/health/liveness"
      ]
    }
  ]
}

Validation Checklist

When reviewing a policy, check:

  1. DENY before ALLOW — while the framework handles ordering, writing DENY statements first improves readability
  2. SpEL templates#{principal.id} is valid; {principal.id} is NOT (conflicts with path variables)
  3. Path variables — use {varName} syntax, access via request.path.var.varName in conditions
  4. Wildcard with conditions"action": "*" alone allows everything; always pair with a condition
  5. Negate logicregular matcher has negate field; other matchers don't
  6. Bool structureand and or are sibling fields, not nested
  7. Part paths — must be valid: request.*, context.principal.*, request.path.var.*, request.attributes.*
  8. Policy typeglobal policies apply to all requests; custom policies are attached to specific users/roles
Install via CLI
npx skills add https://github.com/Ahoo-Wang/CoSec --skill cosec-policy-author
Repository Details
star Stars 43
call_split Forks 4
navigation Branch main
article Path SKILL.md
More from Creator