Home/Security checks

Security checks

Before a PR merges, prove the app still enforces who may access what. Talooner checks the PR's preview against an access policy in your repo, and a rule blocks the merge on any violation.

coming soon

Broken access control tops the OWASP Top 10. A refactor that drops one ownership check looks fine in review, and it passes every test that signs in as the right user. Security checks catch it before the merge. CI starts the PR's build as a throwaway preview, and Talooner checks it against an access policy declared in the repository. One to three cooperating agents each act as a different role. Results come back as facts, and a rule blocks the merge on any violation.

The model plans and proposes scenarios. It never decides the outcome: a finding counts only when the runner reproduces it deterministically.

Security check loop: agents acting as guest, member_a and admin probe the PR preview; a cross-tenant request succeeds, the runner reproduces it, and the merge is blocked.

What it doesn't cover

What you provide in CI

  1. A preview deployment of the PR, reachable from the job, passed in as TALOONER_ACCESS_TARGET (e.g. https://pr-412.preview.acme.dev).

  2. Seed data and test accounts, created by your own CI step. Credentials reach the runner as one masked variable, TALOONER_ACCESS_ACCOUNTS (base64 YAML):

    login:
      url: /api/session          # how a role signs in; the runner keeps the session
      body: '{"email":"{{email}}","password":"{{password}}"}'
    roles:
      guest:      {}             # no session
      member_a:   { email: a@test.acme.dev, password: … , tenant: team-a }
      member_b:   { email: b@test.acme.dev, password: … , tenant: team-b }
      admin:      { email: admin@test.acme.dev, password: … }
  3. The policy, in the repository: the .talooner/access/ folder.

The policy

The policy is a folder of plain tln files, loaded together. Split it however your app is split: one file per area keeps each rule next to the team that owns it.

.talooner/access/
├── guests.tln
├── tenants.tln
└── admin.tln

Each route or resource the runner reaches is a record. The role the agent is acting as arrives as context.actor and context.tenant. Anything a rule blocks must stay out of reach for that role:

Tenant isolation: an agent acting as member_a probes team-b's data and is refused with 403.
// .talooner/access/guests.tln
// Only sign-in and health checks are open without a session.
strict rule "Guests stay out of the API" {
  for records where type == "endpoint"
    and attr "path" starts_with "/api/"
    and attr "path" not in ["/api/session", "/api/health"]
    and context.actor == "guest"
  block "access"
  reason "API routes need a session"
}
// .talooner/access/tenants.tln
// One tenant never sees another tenant's data.
strict rule "Members stay in their own tenant" {
  for records where type == "project"
    and context.actor == "member"
    and attr "tenant" != context.tenant
  block "access"
  reason "cross-tenant access"
}
// .talooner/access/admin.tln
// Admin screens are for admins.
strict rule "Admin area is admin-only" {
  for records where type == "endpoint"
    and attr "path" starts_with "/admin"
    and context.actor != "admin"
  block "access"
  reason "admin-only route"
}

The rules are strict, so nothing else in the ruleset can override them.

Gating the merge

Every violation the runner reproduces becomes a fact on the PR. Your review ruleset gates on it like on any other fact:

rule "Block on access violations" {
  for records where type == "pr"
    and attr "security.violations_count" > 0
  block "merge"
  do block "pr.merge"
  do comment "pr" "Access policy violated: {attr.security.violations}. Each one reproduced against the preview"
  reason "access policy violated"
  priority CRITICAL
}

Where this is headed: red-team agents

The roles above are fixed probes. The direction we're exploring is a group of agents that collaborate to discover access and authorization flaws — a multi-agent red team, pointed only at software you've authorized and running in the preview's isolated sandbox. Each agent has a specialised role and shares findings through a private channel; a coordinator hands out the work and uses what comes back to decide what to look at next.

Scout, Analyst, Tester and Critic agents push finding cards into a shared channel — an endpoint, a missing authorization check, a reproduced response — and the Critic verifies them, all inside an authorized sandbox.

The agents

AgentResponsibility
ScoutMaps the app's surface and flags components worth a closer look
AnalystReads the code and forms vulnerability hypotheses
TesterRuns controlled, benign tests against the isolated copy
CriticIndependently checks findings and throws out false positives

The coordinator distributes the work and uses the agents' feedback to choose the next thing to investigate.

Black box or white box

How much the agents get to see is up to you, set per repo.

Because the PR gives Talooner both the preview and the repository, it can run either — or white box to form the hypothesis and black box to prove it against the live app.

Limits

The red team runs inside the CI job, so it stays bounded and predictable:

Hit a limit and the run ends cleanly with a partial result — never a hang, and never a merge blocked just because the budget ran out.

A collaboration cycle

  1. Scout spots an API endpoint with unusual input handling.
  2. Analyst reads the implementation behind it and proposes a possible authorization flaw.
  3. Tester runs benign cases against a locally isolated copy of the app.
  4. Critic checks whether the observed behaviour actually breaks the intended security policy.
  5. Coordinator consolidates the evidence and recommends a fix or another test.

Everything stays in bounds: the targets are ones you've authorized, and the whole run happens in an isolated sandbox with audit logs.

Want security checks on your repos early? Tell us.

Want this guarding your repos?

Start a free demo