agentsclimarketplace

Cosec troubleshoot

Skill Ahoo-Wang/skills/sources/CoSec/skills/cosec-troubleshoot

Use when diagnosing CoSec authentication or authorization failures such as unexpected 401/403 responses, denied requests that should be allowed, policies not loading, JWT token rejection, matcher mismatches, or unclear access decisions.From its SKILL.md

Install
npx -y skills add Ahoo-Wang/skills --skill cosec-troubleshoot

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

2 things to look at

  • reads credentialsReads from 1 credential source: `cosec.jwt.secret`.
  • 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.

SKILL.md

7.9 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it

CoSec Troubleshooting Guide

This skill helps you debug authorization issues in CoSec. When a request gets an unexpected result (403, 401, or is allowed when it shouldn't be), follow this systematic approach.

Step 1: Enable Debug Logging

The fastest way to understand authorization decisions is debug logging on SimpleAuthorization:

logging:
  level:
    me.ahoo.cosec.authorization.SimpleAuthorization: debug

This logs the full evaluation chain: root check → blacklist → global policies → principal policies → role permissions → final result.

For more granular tracing:

logging:
  level:
    me.ahoo.cosec.policy: debug
    me.ahoo.cosec.authentication: debug
    me.ahoo.cosec.jwt: debug

Step 2: Understand the Evaluation Order

SimpleAuthorization evaluates in this order, stopping at the first definitive result:

1. Root user check
   └─ If principal.id == "cosec" → ALLOW (bypass everything)

2. Blacklist check
   └─ If principal is blacklisted → EXPLICIT_DENY

3. Global policies (type: "global")
   └─ For each global policy:
      a. Check policy-level condition → skip if no match
      b. Check DENY statements → EXPLICIT_DENY if any matches
      c. Check ALLOW statements → ALLOW if any matches
   └─ First definitive result wins

4. Principal-specific policies
   └─ Policies attached to the user (via policy IDs on the principal)
   └─ Same evaluation as global policies

5. Role-based app permissions
   └─ Evaluate role permissions for the request's appId/spaceId
   └─ Only applies when request has an appId

6. Default → IMPLICIT_DENY

Each step uses switchIfEmpty to fall through to the next if no match is found.

Step 3: Common Issues and Fixes

All requests return 403

Symptoms: Every endpoint returns 403, even public ones.

Likely causes:

  1. No policy files loaded — check cosec.authorization.local-policy.enabled=true
  2. Policy files don't match the location pattern — default is classpath:cosec-policy/*-policy.json
  3. Policy JSON syntax error — check startup logs for deserialization errors

Fix:

cosec:
  authorization:
    local-policy:
      enabled: true
      locations: classpath:cosec-policy/*-policy.json

Specific endpoint returns 403 when it should be public

Symptoms: Most endpoints work, but a new public endpoint returns 403.

Cause: No ALLOW statement matches the endpoint. By default, CoSec uses implicit deny — anything not explicitly allowed is denied.

Fix: Add a statement for the endpoint:

{
  "name": "NewPublicEndpoint",
  "action": "/api/new-endpoint"
}

Request allowed when it should be denied

Symptoms: A request that should be blocked gets through.

Likely causes:

  1. DENY statement doesn't match — check action pattern and condition
  2. Another ALLOW statement matches first (but DENY should take precedence)
  3. Root user bypass — check if the user ID is "cosec"

Debug: Enable debug logging and check which statement matched.

JWT token rejected

Symptoms: Requests with valid JWT tokens return 401.

Likely causes:

  1. cosec.jwt.secret doesn't match the token issuer's secret
  2. cosec.jwt.algorithm doesn't match the token's algorithm
  3. Token is expired
  4. Token format is wrong (not a standard JWT)

Check:

cosec:
  jwt:
    algorithm: hmac256    # must match the signing algorithm
    secret: exact-same-secret-used-by-issuer

Policies not loading from local files

Symptoms: Startup succeeds but policies don't take effect.

Checklist:

  1. File location: src/main/resources/cosec-policy/ (not resources/main/...)
  2. File naming: must match *-policy.json pattern
  3. Property: cosec.authorization.local-policy.enabled=true
  4. JSON validity: parse errors are logged at startup
  5. Policy type: must be "global" for the policy to apply to all requests

Rate limiter not working

Symptoms: Rate limiting conditions are ignored.

Cause: Rate limiters require a shared state. In a distributed setup, you need Redis-backed caching (cosec-cocache).

Fix: Add the cocache dependency and configure Redis.

Path variables not matching

Symptoms: /user/123 doesn't match /user/{id}.

Check:

  1. Use {varName} not :varName (Spring WebFlux style)
  2. Access the variable via request.path.var.varName in conditions
  3. Ensure the path pattern is correct (no trailing slash mismatch)

SpEL template not evaluating

Symptoms: #{principal.id} is treated as a literal string.

Cause: SpEL templates use #{} syntax. {} alone is a path variable, not SpEL.

Fix: Use #{principal.id} not {principal.id}.

Condition part path is wrong

Symptoms: Condition always returns false.

Valid part paths:

  • request.path.var.{name} — path variable
  • request.remoteIp — client IP address
  • request.origin — Origin header
  • request.method — HTTP method
  • request.attributes.{key} — request attributes
  • request.headers.{name} — request header
  • context.principal.id — user ID
  • context.principal.attributes.{key} — principal attribute

Common mistakes:

  • request.ip (wrong) → request.remoteIp (correct)
  • principal.id (wrong) → context.principal.id (correct)
  • request.pathVariable.id (wrong) → request.path.var.id (correct)

Step 4: Testing Policies Locally

Unit test with SimpleAuthorization

@Test
fun `test policy evaluation`() {
    val policyLoader = LocalPolicyLoader("classpath:cosec-policy/test-policy.json")
    val policies = policyLoader.load()

    val evaluator = DefaultPolicyEvaluator(policies)

    val request = mockk<Request> {
        every { path } returns "/api/users/123"
        every { method } returns "GET"
        every { remoteIp } returns "192.168.1.1"
    }

    val principal = mockk<CoSecPrincipal> {
        every { id } returns "user-123"
        every { authenticated } returns true
        every { roles } returns setOf("user")
    }

    val context = mockk<SecurityContext> {
        every { [email protected] } returns principal
    }

    val result = evaluator.evaluate(request, context)
    assertThat(result.authorized).isTrue()
}

Test specific matcher

@Test
fun `test path action matcher`() {
    val factory = PathActionMatcherFactory()
    val matcher = factory.create(Configuration.of("pattern" to "/api/users/*"))

    val request = mockk<Request> {
        every { path } returns "/api/users/123"
        every { method } returns "GET"
    }

    assertThat(matcher.match(request, mockk())).isTrue()
}

Step 5: Request Attributes for Debugging

When debugging, inspect the request attributes that CoSec sets:

  • COSEC_SECURITY_CONTEXT — the parsed security context
  • request.attributes.ipRegion — IP geolocation (if cosec-ip2region is enabled)

In a WebFlux handler:

@GetMapping("/debug/whoami")
fun whoami(exchange: ServerWebExchange): Mono<Map<String, Any?>> {
    val context = exchange.getAttribute<SecurityContext>(COSEC_SECURITY_CONTEXT)
    return Mono.just(mapOf(
        "principal" to context?.principal?.id,
        "authenticated" to context?.principal?.authenticated,
        "roles" to context?.principal?.roles,
        "tenant" to context?.tenant?.tenantId
    ))
}

Quick Reference: Authorization Results

ResultauthorizedMeaning
ALLOWtrueExplicitly allowed by a policy statement
EXPLICIT_DENYfalseExplicitly denied by a DENY statement
IMPLICIT_DENYfalseNo statement matched (default deny)
TOKEN_EXPIREDfalseJWT token has expired
TOO_MANY_REQUESTSfalseRate limiter exceeded

What ships with it: 1 file

211 B alongside SKILL.md

agents/

Keep looking

Skills are one crate of 325,949. 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.