agentsclimarketplace

Cosec custom matcher

Skill Ahoo-Wang/skills/plugins/ahoo-cosec-skills/skills/cosec-custom-matcher

A central aggregation repository for Agent Skills from Ahoo-Wang's open source projects.

Install
npx -y skills add Ahoo-Wang/skills --skill cosec-custom-matcher

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when extending CoSec policy matching with custom ActionMatcher or ConditionMatcher implementations, condition types, matcher factories, ServiceLoader SPI registration, or Spring bean matcher registration.

SKILL.md

8.1 KB, as published. Nobody here has run it

CoSec Custom Matcher Development

This skill helps you create custom ActionMatcher and ConditionMatcher implementations to extend CoSec's policy evaluation logic. CoSec uses Java SPI (ServiceLoader) to discover matcher factories.

Architecture Overview

Policy matching has two sides:

  • ActionMatcher — determines if a request's action (path + method) matches a policy pattern
  • ConditionMatcher — determines if contextual conditions are met (user attributes, request properties, etc.)

Both extend RequestMatcher and are created by corresponding factory classes registered via SPI.

Policy
├── condition: ConditionMatcher (policy-level gate)
└── statements[]
    ├── Statement (effect: DENY)
    │   ├── action: ActionMatcher
    │   └── condition: ConditionMatcher
    └── Statement (effect: ALLOW)
        ├── action: ActionMatcher
        └── condition: ConditionMatcher

Creating a Custom ConditionMatcher

Step 1: Implement the ConditionMatcher

package com.example.cosec.condition

import me.ahoo.cosec.api.context.SecurityContext
import me.ahoo.cosec.api.context.request.Request
import me.ahoo.cosec.api.policy.ConditionMatcher
import me.ahoo.cosec.api.configuration.Configuration

class PremiumUserConditionMatcher(
    override val configuration: Configuration
) : ConditionMatcher {

    override val type: String = "premiumUser"

    override fun match(request: Request, securityContext: SecurityContext): Boolean {
        val isPremium = securityContext.principal.attributes["premium"]
        return isPremium == "true"
    }
}

Key points:

  • type — unique string identifier used in policy JSON
  • configuration — arbitrary key-value config passed from the policy JSON
  • match() — return true if the condition is satisfied

Step 2: Implement the Factory

package com.example.cosec.condition

import me.ahoo.cosec.api.configuration.Configuration
import me.ahoo.cosec.api.policy.ConditionMatcher
import me.ahoo.cosec.policy.condition.ConditionMatcherFactory

class PremiumUserConditionMatcherFactory : ConditionMatcherFactory {

    override val type: String = "premiumUser"

    override fun create(configuration: Configuration): ConditionMatcher {
        return PremiumUserConditionMatcher(configuration)
    }
}

Step 3: Register via SPI

Create file: src/main/resources/META-INF/services/me.ahoo.cosec.policy.condition.ConditionMatcherFactory

com.example.cosec.condition.PremiumUserConditionMatcherFactory

Step 4: Use in Policy JSON

{
  "name": "PremiumEndpoints",
  "action": "/api/premium/**",
  "condition": {
    "premiumUser": {}
  }
}

With configuration:

{
  "name": "TieredAccess",
  "action": "/api/**",
  "condition": {
    "premiumUser": {
      "minTier": "gold"
    }
  }
}

Access configuration in the matcher:

val minTier = configuration.getRequiredString("minTier")

Creating a Custom ActionMatcher

Step 1: Implement the ActionMatcher

package com.example.cosec.action

import me.ahoo.cosec.api.context.SecurityContext
import me.ahoo.cosec.api.context.request.Request
import me.ahoo.cosec.api.policy.ActionMatcher
import me.ahoo.cosec.api.configuration.Configuration

class HttpMethodActionMatcher(
    override val configuration: Configuration
) : ActionMatcher {

    override val type: String = "httpMethod"

    private val allowedMethods: Set<String> = configuration.getRequiredString("methods")
        .split(",")
        .map { it.trim().uppercase() }
        .toSet()

    override fun match(request: Request, securityContext: SecurityContext): Boolean {
        return request.method.uppercase() in allowedMethods
    }
}

Step 2: Implement the Factory

package com.example.cosec.action

import me.ahoo.cosec.api.configuration.Configuration
import me.ahoo.cosec.api.policy.ActionMatcher
import me.ahoo.cosec.policy.action.ActionMatcherFactory

class HttpMethodActionMatcherFactory : ActionMatcherFactory {

    override val type: String = "httpMethod"

    override fun create(configuration: Configuration): ActionMatcher {
        return HttpMethodActionMatcher(configuration)
    }
}

Step 3: Register via SPI

Create file: src/main/resources/META-INF/services/me.ahoo.cosec.policy.action.ActionMatcherFactory

com.example.cosec.action.HttpMethodActionMatcherFactory

Step 4: Use in Policy JSON

{
  "name": "ReadOnlyAccess",
  "action": {
    "httpMethod": {
      "methods": "GET,HEAD,OPTIONS"
    }
  }
}

Accessing Configuration Values

The Configuration interface provides typed accessors:

// Required (throws if missing)
val value: String = configuration.getRequiredString("key")

// Optional with default
val value: String = configuration.get("key", "default")

// Nested configuration
val nested: Configuration = configuration.getRequiredConfiguration("nested")

Accessing Request and Context Data

Request properties

request.path           // URL path
request.method         // HTTP method
request.remoteIp       // client IP
request.origin         // Origin header
request.referer        // Referer header
request.appId          // application ID
request.spaceId        // space ID
request.deviceId       // device ID
request.requestId      // request ID
request.getHeader("X-Custom")    // any header
request.getQuery("param")        // query parameter
request.getCookieValue("name")   // cookie value

SecurityContext properties

securityContext.principal                    // CoSecPrincipal
securityContext.principal.id                 // user ID
securityContext.principal.authenticated      // boolean
securityContext.principal.anonymous          // boolean
securityContext.principal.roles              // Set<String>
securityContext.principal.policies           // Set<String>
securityContext.principal.attributes         // Map<String, String>
securityContext.tenant                       // Tenant info
securityContext.attributes                   // MutableMap<String, Any>

Built-in ConditionMatcher Types Reference

For reference, here are all built-in types:

TypeDescriptionKey Config
authenticatedUser must be logged in
inRoleUser must have rolevalue: role name
inTenantMust be from tenantvalue: tenant ID
eqExact matchpart, value
containsSubstring matchpart, value
startsWithPrefix matchpart, value
endsWithSuffix matchpart, value
inValue in listpart, value: array
regularRegex matchpart, pattern, negate
pathPath pattern matchpart, pattern, options
boolBoolean logicand: array, or: array
spelSpring Expressionexpression
ognlOGNL expressionexpression
rateLimiterRate limitingpermitsPerSecond
groupedRateLimiterGrouped rate limitpermitsPerSecond, groupKey

Built-in ActionMatcher Types Reference

TypeDescriptionKey Config
pathURL path matchingpattern, method, options
allWildcardmethod (optional)
compositeOR combinationarray of matchers

Spring Registration (Alternative to SPI)

You can also register matcher factories as Spring beans. The MatcherFactoryRegister auto-configuration picks them up from the ApplicationContext:

@Configuration
class CustomMatcherConfig {

    @Bean
    fun premiumUserConditionMatcherFactory(): ConditionMatcherFactory {
        return PremiumUserConditionMatcherFactory()
    }

    @Bean
    fun httpMethodActionMatcherFactory(): ActionMatcherFactory {
        return HttpMethodActionMatcherFactory()
    }
}

This approach is simpler when your matcher needs Spring dependencies (e.g., a database or external service).

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.