From cosec-policy-author
Author, validate, and explain CoSec security policy JSON files. Use this skill whenever the user mentions writing, creating, editing, or debugging CoSec policies, policy statements, action matchers, condition matchers, or authorization rules. Also use when users ask about policy JSON format, how to allow/deny specific endpoints, how to set up role-based access, or how to configure rate limiting in CoSec policies.
How this skill is triggered — by the user, by Claude, or both
Slash command
/cosec-policy-author:cosec-policy-authorThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
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.
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.
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": [ ... ]
}
| 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 |
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) |
This means you should write DENY rules before ALLOW rules in the statements array for clarity, though the framework handles ordering internally.
The action field defines which requests a statement applies to. There are several formats:
"action": "/api/users"
Matches the exact path. Supports Spring path patterns with wildcards and variables.
"action": "/user/#{principal.id}/*"
#{principal.id} is evaluated at match time against the current security context principal.
"action": "/user/{id}"
Matches path segments. Access the variable in conditions via request.path.var.id.
"action": ["/auth/register", "/auth/login", "/auth/logout"]
Matches if ANY path in the array matches.
"action": "*"
Matches all requests. Use with conditions to restrict scope.
"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.
"action": {
"all": {
"method": "GET"
}
}
Matches all GET requests regardless of path.
"action": {
"composite": [
"/api/public/*",
{
"path": {
"method": "POST",
"pattern": "/api/webhook/*"
}
}
]
}
The condition field adds additional constraints beyond path matching. All condition types:
"condition": { "authenticated": {} }
"condition": { "inRole": { "value": "admin" } }
"condition": { "inTenant": { "value": "tenant-abc" } }
"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 variablerequest.remoteIp — client IPrequest.origin — request originrequest.method — HTTP methodrequest.attributes.{key} — request attributes (e.g., request.attributes.ipRegion)context.principal.id — current user IDcontext.principal.attributes.{key} — principal attributesThe value field supports SpEL templates like #{principal.id}.
"condition": {
"contains": {
"part": "request.attributes.ipRegion",
"value": "上海"
}
}
"condition": {
"startsWith": {
"part": "request.attributes.ipRegion",
"value": "中国"
}
}
"condition": {
"in": {
"part": "context.principal.id",
"value": ["adminId", "developerId"]
}
}
"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).
"condition": {
"path": {
"part": "request.remoteIp",
"pattern": "192.168.0.*",
"options": {
"caseSensitive": false,
"separator": ".",
"decodeAndParseSegments": false
}
}
}
"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.
"condition": {
"spel": {
"expression": "#principal.attributes['vip'] == 'true'"
}
}
"condition": {
"rateLimiter": {
"permitsPerSecond": 10
}
}
"condition": {
"groupedRateLimiter": {
"permitsPerSecond": 100,
"groupKey": "context.principal.id"
}
}
{
"id": "public-api",
"name": "Public API",
"type": "global",
"tenantId": "(platform)",
"statements": [
{
"name": "PublicEndpoints",
"action": ["/auth/login", "/auth/register", "/health"]
}
]
}
{
"id": "admin-api",
"name": "Admin API",
"type": "global",
"tenantId": "(platform)",
"statements": [
{
"name": "AdminOnly",
"action": "/admin/**",
"condition": { "inRole": { "value": "admin" } }
}
]
}
{
"name": "OwnResourcesOnly",
"action": "/api/users/{id}/**",
"condition": {
"eq": {
"part": "request.path.var.id",
"value": "#{principal.id}"
}
}
}
{
"name": "BlockExternalIp",
"effect": "deny",
"action": "*",
"condition": {
"regular": {
"negate": true,
"part": "request.remoteIp",
"pattern": "^(10\\.0\\.0\\.|192\\.168\\.)"
}
}
}
{
"name": "RestrictOrigin",
"effect": "deny",
"action": "*",
"condition": {
"regular": {
"negate": true,
"part": "request.origin",
"pattern": "^https://(app\\.example\\.com|admin\\.example\\.com)"
}
}
}
{
"name": "RateLimitedApi",
"action": "/api/**",
"condition": {
"bool": {
"and": [
{ "authenticated": {} },
{ "rateLimiter": { "permitsPerSecond": 10 } }
]
}
}
}
{
"id": "(health-probe)",
"name": "Health Probe",
"type": "global",
"tenantId": "(platform)",
"statements": [
{
"name": "actuator",
"action": [
"/actuator/health",
"/actuator/health/readiness",
"/actuator/health/liveness"
]
}
]
}
When reviewing a policy, check:
#{principal.id} is valid; {principal.id} is NOT (conflicts with path variables){varName} syntax, access via request.path.var.varName in conditions"action": "*" alone allows everything; always pair with a conditionregular matcher has negate field; other matchers don'tand and or are sibling fields, not nestedrequest.*, context.principal.*, request.path.var.*, request.attributes.*global policies apply to all requests; custom policies are attached to specific users/rolesProvides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub ahoo-wang/skills