Skip to content

Latest commit

 

History

103 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Adversary Actions

Reusable GitHub Actions for pushing and running Doomer adversaries.

Action Status Purpose
version Available Synchronize release metadata and runtime identity, rebuild, verify, and commit from a release version.
push Available Validate, build, package, and push an adversary to an OCI registry.
run Available Run one or more adversaries against the checked-out repository.

Version an adversary

The version action treats a v-prefixed release version as the source of truth. It updates adversary.yaml, synchronizes npm package metadata when present, safely updates a single literal version property in the new Adversary({...}) initializer, rebuilds Node projects, and imports the built runtime to require createApp().version to equal the release version. Changed, already tracked dist/ artifacts are included in the metadata commit. Reruns verify and reuse an existing version commit instead of creating another one.

- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
  with:
    fetch-depth: 0
    persist-credentials: false

- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
  with:
    node-version: 22

- name: Synchronize release metadata
  id: version
  uses: doomerlabs/actions/version@v1
  with:
    tag: ${{ github.ref_name }}
    token: ${{ secrets.RELEASE_GITHUB_TOKEN }}

- name: Push
  uses: doomerlabs/actions/push@v1
  with:
    auth-mode: token
    token: ${{ secrets.ADVERSARY_SERVICE_ACCOUNT_TOKEN }}
    registry-namespace: your-team-slug
    repository-name: ${{ steps.version.outputs.name }}
    push-latest: true

Use a fine-grained GitHub token limited to repository contents read/write. The action stores Git authentication only for its fetch and push operations, removes it before returning, never force-pushes, and never receives the registry credential. sync-npm: auto updates package.json and package-lock.json when package.json exists; set it to false for non-npm adversaries or true to require npm metadata.

For Node adversaries, the runtime command must identify a project-relative JavaScript entrypoint and the built module must export createApp(). A literal runtime version is synchronized with a token-aware source edit; computed or omitted versions are left untouched and accepted only when the rebuilt app reports the correct version (for example, when a future SDK infers it from package metadata). Multiple initializers and runtimes that cannot prove their identity fail closed. Non-Node adversaries keep the metadata-only behavior. Dependency installation and builds use the repository's npm, pnpm, or Yarn lockfile; an existing build script is run when present.

Prepare before tagging

Existing tag-triggered workflows remain supported, but they necessarily build from a working tree that differs from the tag when a version update is needed. For signed tag/runtime identity, run this action on the release branch before creating the tag: pass the intended v<version> as tag, wait for the action to push its commit, then create the annotated or signed tag at ${{ steps.version.outputs.commit }}. The existing release workflow should become verify-and-publish-only for that tag. This ordering makes the tagged tree, rebuilt runtime, registry bytes, and protocol version identical.

The preparation workflow should check out the release branch itself (not a tag) with full history, invoke version with the intended tag text, and hand its commit output to authorized signing/release tooling. That tooling must fetch the pushed commit and create the tag at that exact object; it should reject an existing tag or a branch that advanced unexpectedly. The tag-triggered workflow then validates and publishes without mutating source.

During migration, do not create the tag until the version step succeeds. The backward-compatible tag-triggered mode still fixes and verifies the published runtime and main-branch metadata, but it cannot retroactively change the already-created tag object.

Version inputs

Input Required Default Description
tag yes — Intended release tag formatted as v<semantic-version>; it may be prepared before the tag exists.
path no . Adversary project directory.
branch no main Branch that receives the version commit.
token yes — Fine-grained GitHub token with repository contents read/write.
sync-npm no auto Synchronize npm metadata when present (auto, true, or false).

Version outputs

Output Description
name OCI-compatible name from adversary.yaml.
version Semantic version without the tag's v prefix.
changed Whether this invocation created and pushed a version commit.
commit Version bump commit, or current branch commit when unchanged.

Push an adversary

The push action installs a Doomer CLI release, verifies the release archive against checksums.txt, validates the project, packages it, and pushes both the OCI image manifest and adversary-manifest referrer. Private publishes to the authenticated team namespace on the Doomer registry are signed automatically; the publisher receives only the signature and public team delegation, never a private key.

name: Push adversary

on:
  push:
    tags:
      - "v*"

permissions:
  contents: read
  id-token: write

jobs:
  push:
    runs-on: depot-ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
        with:
          node-version: 22
          cache: npm

      - name: Push
        id: push
        uses: doomerlabs/actions/push@v1
        with:
          path: .
          registry-namespace: your-team-slug
          repository-name: security-reviewer
          push-latest: true

      - name: Report digest
        run: echo "Pushed ${{ steps.push.outputs.reference }} at ${{ steps.push.outputs.digest }}"

When cli-version is omitted, the action installs its pinned default, 2026.9.30-beta.3. Set cli-version explicitly to test a different release. Pin the action itself to an exact release tag or full commit SHA for reproducible CI.

The local builder installs dependencies from package-lock.json, pnpm-lock.yaml, or yarn.lock; configure the matching Node runtime before invoking the action. pnpm and Yarn installs require Corepack, which is not bundled with Node.js 25 and later; install Corepack separately on those runtimes. For reproducible pnpm or Yarn installs, pin the exact tool version in the packageManager field of package.json.

Authentication

The default auth-mode: auto requests the job identity with audience https://doomer.ai, exchanges it for a ten-minute team credential, and deletes the temporary CLI profile afterward. Add permissions: id-token: write, trust the repository under the team page, and pass the team slug as registry-namespace. This works both in GitHub Actions and native Depot CI workflows; Depot identities can also be pinned to the Depot organization ID. For v1 compatibility, auto selects token authentication when the token input is populated; explicit oidc never falls back to a long-lived token.

For CI systems without compatible OIDC, auth-mode: token accepts an Doomer service-account token. Create one with registry:push, store it as a CI secret, and pass it through the token input.

For an interactive run, set auth-mode: oauth. The CLI prints a device-login URL and code and waits for approval through your normal OAuth login. The device request currently expires after ten minutes.

Set auth-mode: existing to skip login. This supports a runner with a preconfigured CLI profile or an external OCI registry authenticated through Docker’s credential store. When profile is omitted, the action uses the CLI's default profile; set profile explicitly to use a different preconfigured profile. Use remote-reference for an explicit registry destination:

- uses: doomerlabs/actions/push@v1
  with:
    cli-version: 2026.9.30-beta.3
    auth-mode: existing
    remote-reference: ghcr.io/acme/dockerfile:0.1.0

For hosted pushes, repository-name overrides the remote name independently of the name in adversary.yaml. It may be nested, such as go/security, and is always rooted under the authenticated team namespace (adversarylabs/go/security). A value already rooted at that same namespace is not duplicated. library/* is reserved for server-side official and partner promotion. The action combines registry-host (default registry.doomer.ai), registry-namespace, the repository name, and the packaged manifest version. Set push-latest: true to push the same digest under latest as well. Use remote-reference instead when the complete versioned destination must be supplied explicitly; it cannot be combined with repository-name.

Inputs

Input Required Default Description
cli-version no 2026.9.30-beta.3 Exact Doomer CLI release tag.
path no . Adversary project directory.
builder no local local or docker package builder.
install-dependencies no true Install dependencies from a supported lockfile before local packaging. Set to false when already installed; ignored by the Docker builder.
name no — Local artifact-name override.
remote-reference no — Fully qualified OCI destination.
repository-name no — Remote repository name within registry-namespace; preserves the packaged version as its tag.
push-latest no false Also push the same artifact with the latest tag.
api-url no hosted API API endpoint used for login and registry token exchange.
profile no push-action for token/OAuth; CLI default for existing CLI credential profile.
auth-mode no auto auto uses a supplied token or otherwise OIDC; oidc requires GitHub Actions or Depot CI identity; token, oauth, and existing select those explicit flows.
token with token auth — Service-account token supplied through a CI secret. For v1 compatibility, supplying it without auth-mode selects token auth.
client-name no Doomer push action Name shown on the OAuth device-approval screen.
registry-host no — Registry host override.
registry-namespace with token auth* — Team registry namespace. May be omitted when remote-reference is explicit.

Outputs

Output Description
reference Canonical pushed registry reference.
digest Pushed OCI image manifest digest.
manifest-digest Pushed adversary-manifest referrer digest.
local-reference Canonical reference produced by the package step.
latest-reference Pushed latest reference when push-latest is enabled.
namespace-signature-digest Platform-issued namespace signature referrer digest for a hosted private publish; empty for external or public repositories.
namespace-trust-digest Platform-endorsed team trust referrer digest for a hosted private publish; empty for external or public repositories.

Run adversaries

The run action installs a Doomer CLI release, optionally authenticates for registry pulls, and executes doomer run against the checked-out source. Pull-request runs default to the PR diff, automatically post findings as a submitted GitHub review, and do not fail the check merely because findings exist. Configuration, authentication, network, and execution failures still fail the step. The action pulls accessible adversaries, detects which ones match the change, and runs the selected set. Set adversaries to one or more references to run an explicit set instead, or set all-files: true to review the entire repository. OIDC pulls from the Doomer registry fetch and verify the public team delegation automatically, so a valid hosted private signature can use host execution without the unsafe override. External copies such as GHCR remain untrusted. It supports model-backed adversaries through provider inputs and secrets. Use the same composite action from GitHub Actions or Depot CI (runs-on: depot-ubuntu-latest).

name: Adversary review

on:
  pull_request:

permissions:
  contents: read
  id-token: write
  pull-requests: write

jobs:
  review:
    runs-on: depot-ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          fetch-depth: 0

      - name: Run adversaries
        id: review
        uses: doomerlabs/actions/run@v1
        with:
          path: .
          auth-mode: oidc
          registry-namespace: your-team-slug
          model-provider: openai
          model: gpt-4o
          model-api-key: ${{ secrets.OPENAI_API_KEY }}
          format: json

      - name: Report outcome
        if: always()
        run: |
          echo "outcome=${{ steps.review.outputs.outcome }}"
          echo "findings=${{ steps.review.outputs.findings-count }}"

When cli-version is omitted, the action installs its pinned default, 2026.9.30-beta.3. Set cli-version explicitly to test a different release, and pin the action ref for reproducible CI. path defaults to ..

Artifact cache

The action stores pulled adversaries in a content-addressed repository under data-dir. It still checks the remote catalog and resolves each OCI reference on every run; when the resolved digest is already present, the CLI reuses the local artifact without downloading its layers and verifies it before execution. data-dir defaults to ${RUNNER_TEMP}/adversary-data, and an existing ADVERSARY_DATA_DIR environment value remains supported when the input is omitted.

Cache only this artifact directory. Doomer credentials use the operating system's separate configuration directory and are not written beneath data-dir. The artifact cache does contain the complete contents of private adversaries, so scope access to jobs that are authorized to pull those packages.

Depot CI can persist the directory with a durable cache disk. Use a repository-specific disk name unless cross-repository sharing is intentional:

- name: Mount adversary cache
  uses: depot/cache-mount@v1
  with:
    name: adversary-${{ github.event.repository.id }}-v1
    path: /mnt/doomer

- name: Run adversaries
  uses: doomerlabs/actions/run@v1
  with:
    data-dir: /mnt/doomer
    adversaries: auto
    auth-mode: oidc
    registry-namespace: your-team-slug

Other CI cache implementations can restore and save ${{ runner.temp }}/adversary-data while using the action's default, or mount a different absolute path and pass it through data-dir. Include v1 in the cache key so a future incompatible repository format can move to a fresh cache.

Automatic selection is the default. These are equivalent:

- uses: doomerlabs/actions/run@v1

- uses: doomerlabs/actions/run@v1
  with:
    adversaries: auto

To bypass automatic selection, provide explicit references. Each explicit adversary still applies its own changed-file trigger unless force: true is set:

- uses: doomerlabs/actions/run@v1
  with:
    adversaries: |
      web/nextjs
      web/react

Pull-request scope is inferred from the CI environment. Use base and head to override the inferred diff, or all-files: true to opt into a full-repository scan. base/head and all-files cannot be combined.

Pull-request reviews

On pull_request and pull_request_target events, github-review: auto posts findings through GitHub's GraphQL review API and github-submit: true submits the review as an informational comment. Grant pull-requests: write; the action uses github.token unless github-token is supplied. After a complete successful rerun, the action resolves prior threads from the same GitHub identity when their findings are no longer reported by an adversary that ran again. Set resolve-addressed-comments: false to leave those threads open. Partial or failed reviews never resolve comments. The default summary covers actual findings only and uses the configured model provider for one cross-adversary synthesis; clean adversaries add nothing, and a clean run posts no review. Set include-summary: false to omit that persistent summary while retaining inline findings and findings that cannot be placed on the diff. Set github-review: false to keep results in the job log only.

For CLI releases that support comment voice controls, set comment-tone to direct, neutral, or coaching, comment-conciseness to terse, standard, or explanatory, and comment-politeness and comment-formality independently to low, medium, or high. Politeness also accepts very-low for sharp, evidence-backed criticism of the PR, never its author. Low formality permits occasional mild swearing, never abuse. Omit inputs to use the installed CLI's defaults. These inputs apply when github-review is enabled. Pin cli-version to a release containing all requested controls.

Authentication

Default auth-mode: none skips login so public and local adversaries work without a token. For private pulls, prefer auth-mode: oidc, add permissions: id-token: write, trust the GitHub or Depot repository identity on the team page, and set registry-namespace. The exchanged pull credential lasts ten minutes and the action removes its unique temporary profile afterward.

- uses: doomerlabs/actions/run@v1
  with:
    adversaries: your-team/private-reviewer
    auth-mode: oidc
    registry-namespace: your-team-slug

Use auth-mode: token with a pull-scoped service-account token when OIDC is unavailable. auth-mode: oauth uses interactive device login. auth-mode: existing uses a preconfigured CLI profile or Docker credential store and never logs in or out.

Model-backed adversaries

CI reviews require a model and a working provider credential: a partial review is not a passing review. Provide model-provider (openai, cloudflare, anthropic, fireworks, or camel), model, and model-api-key (a secret). The action maps the key to the selected provider's credential environment variable and never places API keys on the CLI argument list. For Cloudflare AI Gateway, also set cloudflare-account-id; cloudflare-gateway-id is optional. Provider-specific base URL inputs set the corresponding ADVERSARY_*_BASE_URL overrides.

You may instead set ADVERSARY_MODEL, optionally ADVERSARY_MODEL_PROVIDER, and the standard provider credential environment variable on the step. The provider may be omitted only when exactly one supported provider credential is present. Missing configuration fails before the review starts; an unusable credential or another reviewer execution failure that makes the composed review incomplete also fails the step.

Exit codes and findings

The action records the CLI exit code and outcome in its outputs. By default, CLI exit 1 (findings) becomes a successful step after the review is posted; exits 2–4 still fail. An otherwise successful partial review is promoted to exit 3 and outcome: failure. Set fail-on-findings: true only when findings should block the check. With format: json, findings-count and result-file are populated from captured stdout.

The entire review command has a 10-minute wall-clock deadline, including registry pulls, indexing, and all selected adversaries. Set timeout-minutes: 5 for a shorter run or timeout-minutes: 20 for a longer one under the action’s with: inputs. Positive fractional minutes are also supported. On expiry, the action terminates the review processes, allows up to 5 seconds for shutdown, then force-kills remaining processes and fails with exit-code: 124 and outcome: failure, even when fail-on-findings is false. CLI installation and authentication are outside this deadline; a workflow/job timeout can bound those too. The standalone CLI default is unchanged. The existing timeout input remains a separate per-adversary execution limit.

Run inputs

Input Required Default Description
adversaries no auto auto to pull and select matching accessible adversaries, or one or more explicit refs (whitespace or newlines).
cli-version no 2026.9.30-beta.3 Exact Doomer CLI release tag.
path no . Source directory to review.
data-dir no ${RUNNER_TEMP}/adversary-data Absolute directory containing cacheable adversary artifacts. ADVERSARY_DATA_DIR is used as a fallback when set.
base no — Git base ref for change detection.
head no — Git head ref for change detection.
all-files no false Opt into a full-repository scan instead of the inferred PR or branch diff.
github-review no auto auto, true, or false; auto posts on pull-request events.
review-tracks no both both, standard, or adversarial; the latter two require a CLI release with intent routing.
github-submit no true Submit the GitHub review as an informational comment instead of leaving it pending.
include-summary no true Include the aggregate assessment/opinion in the review body; findings are still posted when false.
resolve-addressed-comments no true Resolve prior Adversary threads whose findings disappear after a complete successful rerun.
comment-tone no CLI default direct, neutral, or coaching; requires a CLI release with comment voice controls.
comment-conciseness no CLI default terse, standard, or explanatory; requires a CLI release with comment voice controls.
comment-politeness no CLI default very-low, low, medium, or high; controls bluntness without personal attacks.
comment-formality no CLI default low, medium, or high; low allows occasional mild swearing.
github-token no github.token Token used to post the GitHub review.
builder no local local or docker builder for local adversaries.
build no false Build a local adversary before running.
force no false Run even when file triggers do not match.
format no text text or json output.
keep-temp no false Keep the temporary run directory.
no-network no false Require network isolation for the adversary child.
verbose no false Detailed execution diagnostics.
include-suppressed no false Request suppressed findings when supported.
shell no false UNSAFE host shell in the adversary working directory.
allow-unsafe-host-execution no false Allow unrestricted HostExecutor for an unknown publisher.
timeout-minutes no 10 Positive wall-clock minutes for the entire review command.
timeout no — Max individual adversary execution time (Go duration, for example 10m). Empty or 0 disables this individual limit.
build-timeout no — Max explicit local build time (Go duration).
model-provider unless exactly one provider credential is in the environment — openai, cloudflare, anthropic, fireworks, or camel.
model yes* — Provider model identifier. May instead be supplied through ADVERSARY_MODEL.
model-api-key yes* — Provider API key secret mapped from model-provider. May instead be supplied through the provider's standard environment variable.
openai-base-url no — OpenAI-compatible base URL override.
cloudflare-account-id with Cloudflare — Cloudflare account ID.
cloudflare-gateway-id no — Optional Cloudflare AI Gateway ID.
cloudflare-base-url no — Cloudflare AI REST API base URL override.
anthropic-base-url no — Anthropic-compatible base URL override.
fireworks-base-url no — Fireworks-compatible base URL override.
camel-base-url no — Camel-compatible base URL override.
fail-on-findings no false Fail the step when the review reports findings.
api-url no hosted API API endpoint used for login.
profile no ephemeral run-action-<id> for token/OAuth; CLI default otherwise For existing, the CLI profile to use (never logged out). For token/OAuth, used only as a name prefix for a unique action-owned profile that is removed after the step.
auth-mode no none none, oidc, token, oauth, or existing.
token with token auth — Pull-scoped service-account token secret.
client-name no Doomer run action Name shown on the OAuth device-approval screen.
registry-host no — Registry host override.
registry-namespace no — Team registry namespace for service-account login.

Run outputs

Output Description
exit-code CLI exit code (0–4), or 124 when the action review deadline expires.
findings-count Finding count when format is json; empty for text.
result-file Path to captured JSON stdout when format is json.
outcome success, findings, or failure.

Security model

  • Push: target source is built because packaging is required. Use only on reviewed code and protected release refs. Validation and packaging finish before authentication so target build scripts never see the service-account token. Prefer a push-scoped token.
  • Run: read-only review of the checked-out tree. Prefer a pull-scoped service-account token (or auth-mode: none for public/local adversaries). Do not reuse push credentials in ordinary review jobs.
  • The CLI archive is checksum-verified before execution. Pin cli-version in the caller workflow when exact toolchain reproducibility is required.
  • OIDC credentials expire after ten minutes. OIDC and service-account tokens are passed through standard input, removed from the environment before run/push, and temporary CLI profiles are removed afterward.
  • The run action's data-dir contains package payloads and trust metadata, never credentials. Treat caches containing private adversaries as private data and scope them accordingly.
  • Model API keys are passed only through environment variables (never CLI flags) and are unset from action input env before the CLI is invoked.
  • Neither action requires write permission for the caller repository. Registry authority comes only from the supplied credential flow.

Development

Releasing the actions

Push a new immutable vMAJOR.MINOR.PATCH tag from reviewed main. The release workflow tests that tagged commit, installs its default CLI, updates and verifies the moving major tag (v1 for a v1.x.x release), then publishes the GitHub release. A failed major-tag update prevents release publication. Merging to main alone does not release the actions.

To retry publication for an existing tag, dispatch update-major-tag.yml with the tag input. Reruns preserve existing releases; older tags cannot move the major tag backwards.

Run the deterministic shell test suite locally:

bash test/test.sh

The tests use a local release archive and a fake CLI; they do not contact Doomer or push artifacts.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages