Skip to content

Repository files navigation

AxiomGuard terminal demo

AxiomGuard

Security building blocks for Node.js and TypeScript services.

Zero runtime dependencies · modular imports · guarded outbound fetches · framework adapters · SARIF-ready scanning

CI GitHub package GHCR Node License

AxiomGuard collects backend security controls that are easy to rewrite badly and annoying to install as a dozen unrelated packages: API keys, webhook verification, replay protection, secure cookies, CSRF tokens, CORS policy, defensive headers, SSRF-oriented URL checks and outbound fetches, rate limiting, environment validation, secret-safe logging, path safety and repository scanning.

It stays intentionally small. Core and adapters have no runtime npm dependencies, and security assumptions are documented next to the feature instead of hidden behind a generic “secure by default” claim.

Install

GitHub Packages:

# ~/.npmrc
@axiomnode-lab:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN
npm install @axiomnode-lab/guard

The repository also contains an npmjs publishing path for public distribution. It remains disabled until the npm scope and trusted publisher are configured; see docs/RELEASE.md.

Module map

Import Purpose
/api-keys High-entropy API keys, digests, verification and masking
/webhooks Generic HMAC, GitHub, Stripe-style signed timestamps and replay stores
/cookies Secure cookie serialization and prefix invariants
/cors Strict framework-neutral origin/preflight policy
/csrf Signed, expiring, optionally session-bound CSRF tokens
/headers CSP/nonces, HSTS, cross-origin and defensive headers
/presets Explicit api, web, and isolated header presets
/web SSRF-oriented URL/DNS checks and redirect allowlists
/fetch Redirect-aware guarded Fetch API wrapper with per-hop validation
/rate-limit Fixed-window limiter, Redis adapters and response-header helpers
/logging Key-, pattern- and path-based secret redaction plus PII masking
/env Typed environment parsing, defaults, ranges and allowlists
/filesystem Traversal-safe paths and filename sanitization
/scanner Programmatic secret scanning, baselines, fingerprints and SARIF
/adapters/* Express, Fastify, Hono and Redis integration layers
axiomguard CLI scanner

Use the root export for convenience or subpath imports for a smaller, clearer dependency surface.

Provider-aware webhooks

import {
  MemoryReplayStore,
  verifyGitHubWebhook,
  verifyStripeWebhook,
} from '@axiomnode-lab/guard/webhooks';

const githubOk = verifyGitHubWebhook(
  rawBody,
  req.headers['x-hub-signature-256'],
  process.env.GITHUB_WEBHOOK_SECRET!,
);

const stripeResult = await verifyStripeWebhook(
  rawBody,
  req.headers['stripe-signature'],
  process.env.STRIPE_WEBHOOK_SECRET!,
  {
    toleranceSeconds: 300,
    replayStore: new MemoryReplayStore(),
  },
);

For multiple service instances, replace MemoryReplayStore with a shared store. AxiomGuard includes node-redis and ioredis adapters that use atomic NX + PX claims.

Express, Fastify and Hono

Adapters combine AxiomGuard's header and CORS primitives without taking a runtime dependency on the framework.

import { createExpressSecurityMiddleware } from '@axiomnode-lab/guard/adapters/express';

app.use(createExpressSecurityMiddleware({
  cors: {
    origins: ['https://app.example.com'],
    allowCredentials: true,
    allowMethods: ['GET', 'POST'],
  },
}));

Equivalent adapters are available for Fastify and Hono. See docs/ADAPTERS.md for framework and Redis examples.

Rate limiting

import {
  MemoryRateLimitStore,
  checkRateLimit,
  createRateLimitHeaders,
} from '@axiomnode-lab/guard/rate-limit';

const store = new MemoryRateLimitStore();
const result = await checkRateLimit(`ip:${clientIp}`, {
  limit: 60,
  windowMs: 60_000,
  store,
});

const headers = createRateLimitHeaders(result, {
  policyName: 'api',
});

if (!result.allowed) {
  // Return 429 and include the generated Retry-After/RateLimit fields.
}

The helper can emit the current IETF draft RateLimit-Policy/RateLimit fields together with widely deployed compatibility fields. The draft is not treated as a finalized RFC. The memory store is single-process; shared deployments can use Redis adapters.

Security-header presets

Presets are opt-in and deployment-conscious. HSTS is not silently enabled because it is a deployment commitment; cross-origin isolation is a separate preset because it can break resources that are not prepared for it.

import { createPresetSecurityHeaders } from '@axiomnode-lab/guard/presets';

const webHeaders = createPresetSecurityHeaders('web');
const isolatedHeaders = createPresetSecurityHeaders('isolated');

Secure cookies and CSRF

import { serializeCookie } from '@axiomnode-lab/guard/cookies';
import { createCsrfToken, verifyCsrfToken } from '@axiomnode-lab/guard/csrf';

const cookie = serializeCookie('__Host-session', sessionToken, {
  sameSite: 'Lax',
  maxAge: 3600,
});

const csrf = createCsrfToken(process.env.CSRF_SECRET!, { sessionId: session.id });
const csrfOk = verifyCsrfToken(csrf, process.env.CSRF_SECRET!, {
  sessionId: session.id,
  maxAgeSeconds: 7200,
});

Cookie prefix, SameSite, Secure and partitioned-cookie invariants are validated instead of being emitted in contradictory combinations.

Environment validation and log redaction

import { requireEnv } from '@axiomnode-lab/guard/env';
import { redactSecrets } from '@axiomnode-lab/guard/logging';

const env = requireEnv({
  PORT: { type: 'port', default: 3000 },
  API_URL: 'url',
  MODE: { type: 'string', allowed: ['development', 'staging', 'production'] },
});

const safeEvent = redactSecrets(event, {
  paths: ['req.headers.x-api-key', 'users.*.profile'],
});

Validated configuration is frozen. Redaction never mutates the source object and supports secret-key heuristics, credential patterns and explicit wildcard paths.

SSRF-oriented URL checks

import { assertSafeResolvedUrl } from '@axiomnode-lab/guard/web';

const target = await assertSafeResolvedUrl(userInput, {
  protocols: ['https:'],
  allowedHosts: ['api.example.com'],
});

This blocks common localhost/private/link-local/reserved targets at validation time. It does not eliminate DNS rebinding or time-of-check/time-of-use risk.

Guarded outbound fetches

safeFetch() turns the URL checks into a practical outbound-request primitive. It validates the initial target and every followed redirect, limits redirect depth, applies a total timeout, strips sensitive credentials on cross-origin redirects, and refuses transport-header overrides and unsafe body replay.

import { safeFetch } from '@axiomnode-lab/guard/fetch';

const response = await safeFetch(userSuppliedUrl, {
  protocols: ['https:'],
  allowedHosts: ['api.example.com'],
  maxRedirects: 2,
  timeoutMs: 5_000,
  headers: { accept: 'application/json' },
});

The underlying Fetch implementation can still resolve DNS again when opening the connection, so this is not a complete DNS-rebinding/TOCTOU boundary. High-risk fetchers still need outbound network controls. See docs/SAFE_FETCH.md.

Scanner: text, JSON and SARIF

axiomguard scan .
axiomguard scan . --json
axiomguard scan . --sarif --output axiomguard.sarif
axiomguard scan . --write-baseline .axiomguard-baseline.json

The scanner reports rule, file, line and a non-secret fingerprint but never the matched credential value. Config files and reviewed baselines allow teams to roll it out without permanently hiding moved/new findings. See docs/SCANNER.md.

Programmatic use:

import { findingsToSarif, scanSecrets } from '@axiomnode-lab/guard/scanner';

const findings = await scanSecrets('.');
const sarif = findingsToSarif(findings);

GitHub Action

- uses: actions/checkout@v6
- id: axiomguard
  uses: AxiomNode-lab/AxiomGuard@v0.5.0
  with:
    path: .
    fail-on-findings: 'true'

The action exposes a SARIF path that can be uploaded with github/codeql-action/upload-sarif@v4. Full workflow: docs/GITHUB_ACTION.md.

Container

docker run --rm \
  -v "$PWD:/workspace:ro" \
  ghcr.io/axiomnode-lab/axiomguard:edge scan /workspace

Qualification and delivery

Every pull request qualifies Node.js 20, 22 and 24 with type checking, regression tests, Node 24 coverage, package dry-run, published-subpath import smoke tests and a self scan. CI also runs the repository's composite GitHub Action against itself and validates its SARIF output.

Delivery paths:

main
├── @axiomnode-lab/guard → GitHub Packages
└── ghcr.io/axiomnode-lab/axiomguard:edge → GHCR

GitHub Release
├── GHCR semver/latest + SBOM + provenance
└── npmjs publish (only when explicitly enabled/configured)

The GitHub Packages workflow can also be started manually for diagnostics/retry and reads the exact version back after publishing before it reports success.

What AxiomGuard does not replace

  • a WAF, secrets manager, identity provider or authorization framework
  • Argon2/scrypt/bcrypt password hashing
  • egress firewalling, cloud metadata protection, or destination-pinned networking
  • a dedicated distributed abuse-prevention platform
  • a complete SAST or secrets-scanning platform
  • a security review of the application using it

The package is useful when those boundaries are acceptable and explicit.

Development

git clone https://github.com/AxiomNode-lab/AxiomGuard.git
cd AxiomGuard
npm ci
npm run typecheck
npm test
npm run test:coverage
npm pack --dry-run
npm run scan:self

Read RESEARCH.md, SECURITY.md, docs/ADAPTERS.md, docs/SCANNER.md, docs/SAFE_FETCH.md, and docs/RELEASE.md before changing security-sensitive behavior.

Contributing

Security-sensitive changes should include positive tests, negative tests and a written failure boundary. See CONTRIBUTING.md and CODE_OF_CONDUCT.md.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages