AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Security Headers Audit

skill-kalshamsi-claude-security-skills-security-headers-audit · by kalshamsi

Use when reviewing HTTP security headers, checking a Content-Security-Policy, auditing CORS or HSTS configuration, evaluating X-Frame-Options or Permissions-Policy, inspecting header middleware, or hardening a web application's response headers.

No reviews yet
0 installs
11 views
0.0% view→install

Install

$ agentstack add skill-kalshamsi-claude-security-skills-security-headers-audit

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-kalshamsi-claude-security-skills-security-headers-audit)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Security Headers Audit? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Security Headers Audit

This skill performs static code analysis for HTTP security header misconfigurations across Express/Helmet.js, Nginx, Apache, Next.js, Flask, Django, and Spring Boot projects. HTTP response headers are the first line of defence against a wide class of client-side attacks — clickjacking, MIME-sniffing, cross-site scripting amplification, cross-origin data leakage, and protocol downgrade attacks. A single missing or misconfigured header can expose users to attacks that a compliant browser would otherwise block. This skill audits 10+ header-level controls, maps each finding to CWE and OWASP Top 10:2021 identifiers, and produces UNSAFE/SAFE code pairs across multiple frameworks so developers can apply fixes immediately.

When to Use

  • When the user asks to "audit security headers", "check HTTP headers", "review header config", or "harden web headers"
  • When the user mentions "CSP", "Content-Security-Policy", "unsafe-inline", "unsafe-eval", or "CSP report-only"
  • When the user asks about "CORS", "Access-Control-Allow-Origin", or "cross-origin policy"
  • When the user asks about "HSTS", "Strict-Transport-Security", "HTTPS enforcement", or "preload"
  • When the user asks about "X-Frame-Options", "clickjacking protection", or "frame-ancestors"
  • When the user asks about "X-Content-Type-Options", "MIME sniffing", or "nosniff"
  • When the user asks about "Referrer-Policy", "Permissions-Policy", or "Feature-Policy"
  • When reviewing Express middleware, Nginx server blocks, Apache VirtualHost configs, Flask response objects, or Spring Boot security config
  • When preparing a web application for a security audit, penetration test, or compliance review (PCI-DSS, HIPAA, FedRAMP)
  • When a pull request modifies server configuration, middleware stacks, or HTTP response handling

When NOT to Use

  • When the user wants runtime testing of live server responses (use a DAST tool such as dast-nuclei, OWASP ZAP, or ffuf-web-fuzzing)
  • When the issue is application logic — authentication bypasses, SQL injection, or broken access control — rather than header configuration
  • When the user wants a full vulnerability scan including code-level SAST (use bandit-sast or security-review)
  • When the owasp-security skill already covers the request at a broader OWASP level
  • When the user asks about mobile security, certificate pinning, or mobile app issues — you MUST decline and recommend mobile-security
  • When the user asks about SQL injection, XSS, or input validation in application code — you MUST decline and recommend security-review or bandit-sast

Prerequisites

Tool Installed (Preferred)

No external tool required. This is a pure analysis skill.

All 10+ checks are performed through pattern matching and code inspection of server configuration files, middleware definitions, and application code — no CLI tool needs to be installed, configured, or invoked. The skill works offline and requires no API keys.

Tool Not Installed (Fallback)

This skill is always available as a pure analysis skill. There is no fallback mode because there is no external tool dependency. All checks run directly through code review and pattern analysis.

Workflow

  1. Detect server framework — Inspect project files to determine which frameworks set HTTP headers:
  • package.json containing helmet, cors, express → Express/Helmet.js
  • nginx.conf or sites-available/ directory → Nginx
  • .htaccess or httpd.conf or apache2.conf → Apache
  • next.config.js or next.config.ts → Next.js
  • requirements.txt containing Flask or flask-talisman → Flask
  • requirements.txt containing Django or django-csp → Django
  • pom.xml containing spring-boot or spring-security → Spring Boot
  1. Identify header-relevant files — Locate the files most likely to contain header configuration:
  • Express: app.js, server.js, middleware/, any file calling app.use()
  • Nginx: nginx.conf, conf.d/*.conf, sites-enabled/*
  • Apache: httpd.conf, apache2.conf, .htaccess, VirtualHost blocks
  • Next.js: next.config.js, middleware.ts, API route handlers
  • Flask: app.py, __init__.py, any @app.after_request hook
  • Django: settings.py, middleware.py, MIDDLEWARE list
  • Spring Boot: SecurityConfig.java, WebMvcConfigurer implementations
  1. Run all 10+ security header checks (see Checks section) against each identified file.
  2. For each finding:

a. Determine severity (Critical / High / Medium / Low) using the Reference Tables b. Map to the relevant CWE identifier c. Map to the relevant OWASP Top 10:2021 category d. Record file path and approximate line number or config block e. Document the UNSAFE configuration observed and the corresponding SAFE fix f. Draft a remediation recommendation with framework-specific code

  1. Deduplicate and sort findings by severity: Critical > High > Medium > Low.
  2. Generate the findings report using the Findings Format below.
  3. Summarize — State total findings, breakdown by severity, and top 3 remediation priorities.

Checks


Check 1: Missing or Misconfigured Content-Security-Policy (CSP)

CWE-693 (Protection Mechanism Failure) | A05:2021 - Security Misconfiguration | Severity: High

WHY: A Content-Security-Policy header instructs the browser to only load resources from trusted origins, blocking inline script injection, data-URI attacks, and third-party resource hijacking. Without CSP, a successful XSS attack has unrestricted access. Directives such as unsafe-inline and unsafe-eval negate the policy's XSS protection entirely, and wildcard sources (*) allow an attacker to load resources from any domain they control.

UNSAFE:

// Express — no CSP header set at all
const express = require('express');
const app = express();
// No helmet() or manual Content-Security-Policy — CSP missing entirely
app.get('/', (req, res) => res.send('...'));
// Express/Helmet — CSP disabled or using unsafe directives
const helmet = require('helmet');
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'", '*'],          // wildcard allows any origin
    scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"],  // XSS protection nullified
    styleSrc: ["'self'", "'unsafe-inline'"],
  }
}));
# Nginx — no CSP header in server block
server {
    listen 443 ssl;
    server_name example.com;
    # Missing: add_header Content-Security-Policy "...";
}
# Flask — no CSP header on responses
@app.route('/')
def index():
    return render_template('index.html')
    # No Content-Security-Policy header added

SAFE:

// Express/Helmet — strict CSP with nonce-based inline script allowance
const helmet = require('helmet');
const crypto = require('crypto');

app.use((req, res, next) => {
  res.locals.cspNonce = crypto.randomBytes(16).toString('hex');
  next();
});

app.use((req, res, next) => {
  helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.cspNonce}'`],
      styleSrc: ["'self'"],
      imgSrc: ["'self'", 'data:', 'https://cdn.example.com'],
      connectSrc: ["'self'", 'https://api.example.com'],
      objectSrc: ["'none'"],
      upgradeInsecureRequests: [],
    },
  })(req, res, next);
});
# Nginx — strict CSP header
server {
    listen 443 ssl;
    server_name example.com;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; object-src 'none'; upgrade-insecure-requests;" always;
}

Check 2: Permissive CORS Policy (Access-Control-Allow-Origin: *)

CWE-942 (Permissive Cross-Origin Resource Sharing Policy) | A05:2021 - Security Misconfiguration | Severity: High

WHY: Setting Access-Control-Allow-Origin: * allows any website on the internet to make cross-origin requests to the API and read the response. For APIs serving authenticated data, this combined with Access-Control-Allow-Credentials: true permits session-riding attacks where a malicious page reads sensitive user data. Even without credentials, wildcard CORS on internal APIs exposes business data to any third-party site.

UNSAFE:

// Express — wildcard CORS open to all origins
const cors = require('cors');
app.use(cors()); // defaults to origin: '*'

// Or explicitly:
app.use(cors({ origin: '*' }));
// Express — manual wildcard header
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
  next();
});
# Nginx — wildcard CORS
location /api/ {
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
}
# Flask — wildcard CORS via flask-cors
from flask_cors import CORS
CORS(app)  # defaults to origins='*'

SAFE:

// Express — allowlist-based CORS
const cors = require('cors');
const allowedOrigins = [
  'https://app.example.com',
  'https://admin.example.com',
];
app.use(cors({
  origin: (origin, callback) => {
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true,  // only if needed; never combine with wildcard origin
}));
# Nginx — origin allowlist via map
map $http_origin $cors_origin {
    default "";
    "https://app.example.com" "$http_origin";
    "https://admin.example.com" "$http_origin";
}
server {
    location /api/ {
        add_header 'Access-Control-Allow-Origin' $cors_origin always;
    }
}
# Flask — explicit origin allowlist
from flask_cors import CORS
CORS(app, origins=['https://app.example.com', 'https://admin.example.com'])

Check 3: Missing or Weak HTTP Strict-Transport-Security (HSTS)

CWE-319 (Cleartext Transmission of Sensitive Information) | A02:2021 - Cryptographic Failures | Severity: High

WHY: Without HSTS, a browser that first visits a site over HTTP is vulnerable to SSL-stripping attacks. An on-path attacker intercepts the HTTP request before it can redirect to HTTPS, silently downgrading the session. HSTS instructs browsers to always connect over HTTPS for a specified duration. A max-age under 1 year is insufficient for meaningful protection; missing includeSubDomains leaves subdomains vulnerable; missing preload means first-visit HTTPS is not enforced by browser preload lists.

UNSAFE:

// Express — HSTS missing entirely
const helmet = require('helmet');
app.use(helmet({
  hsts: false,  // HSTS explicitly disabled
}));
// Express — HSTS with too-short max-age (1 day = trivially bypassable)
app.use(helmet.hsts({
  maxAge: 86400,  // 1 day — far too short; attacker can wait for expiry
  // Missing: includeSubDomains, preload
}));
# Nginx — missing HSTS header entirely
server {
    listen 443 ssl;
    # No Strict-Transport-Security header
}
# Flask — no HSTS enforcement
@app.route('/login', methods=['POST'])
def login():
    # No HSTS header — HTTP downgrade possible
    return jsonify({'token': generate_token()})

SAFE:

// Express/Helmet — HSTS with 2-year max-age, includeSubDomains, preload
const helmet = require('helmet');
app.use(helmet.hsts({
  maxAge: 63072000,        // 2 years in seconds
  includeSubDomains: true,
  preload: true,
}));
# Nginx — full HSTS header
server {
    listen 443 ssl;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
}
# Flask — HSTS via after_request hook or flask-talisman
from flask_talisman import Talisman
Talisman(app,
    strict_transport_security=True,
    strict_transport_security_max_age=63072000,
    strict_transport_security_include_subdomains=True,
    strict_transport_security_preload=True,
)

Check 4: Missing X-Content-Type-Options

CWE-16 (Configuration) | A05:2021 - Security Misconfiguration | Severity: Medium

WHY: Without X-Content-Type-Options: nosniff, browsers perform MIME-type sniffing — inferring the content type from the response body rather than the declared Content-Type. An attacker who can upload a file (e.g., a JPEG with embedded JavaScript) can trick the browser into executing it as a script, bypassing CSP. The nosniff directive prevents this by forcing strict MIME-type enforcement.

UNSAFE:

// Express — X-Content-Type-Options header absent
const express = require('express');
const app = express();
// No helmet() — X-Content-Type-Options not sent
app.use(express.static('public'));
# Nginx — X-Content-Type-Options not set
server {
    listen 443 ssl;
    root /var/www/html;
    # Missing: add_header X-Content-Type-Options "nosniff";
}

SAFE:

// Express/Helmet — nosniff enabled (default in helmet())
const helmet = require('helmet');
app.use(helmet()); // X-Content-Type-Options: nosniff included by default

// Or manually:
app.use((req, res, next) => {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  next();
});
# Nginx — nosniff header
server {
    listen 443 ssl;
    add_header X-Content-Type-Options "nosniff" always;
}
# Flask — explicit nosniff header
@app.after_request
def set_security_headers(response):
    response.headers['X-Content-Type-Options'] = 'nosniff'
    return response

Check 5: Missing X-Frame-Options or Permissive CSP frame-ancestors

CWE-1021 (Improper Restriction of Rendered UI Layers or Frames) | A05:2021 - Security Misconfiguration | Severity: Medium

WHY: Without framing protection, an attacker can embed the target page in a hidden ` on a malicious site and trick authenticated users into performing actions they did not intend (clickjacking). X-Frame-Options: DENY or SAMEORIGIN prevents this. The modern equivalent is CSP frame-ancestors 'none' or 'self'`, which offers more granular control. Both should be set for maximum browser compatibility.

UNSAFE:

// Express — no frame protection
const helmet = require('helmet');
app.use(helmet({
  frameguard: false,  // Clickjacking protection disabled
  contentSecurityPolicy: false,
}));
# Nginx — X-Frame-Options absent
server {
    listen 443 ssl;
    # No X-Frame-Options or frame-ancestors directive
}
# Flask — no X-Frame-Options header
@app.after_request
def headers(response):
    # X-Frame-Options not set — clickjacking possible
    return response

SAFE:

// Express/Helmet — deny all framing
const helmet = require('helmet');
app.use(helmet.frameguard({ action: 'deny' }));

// For CSP frame-ancestors (modern, preferred):
app.use(helmet.contentSecurityPolicy({
  directives: {
    frameAncestors: ["'none'"],
    // ... other directives
  }
}));
# Nginx — X-Frame-Options + CSP frame-ancestors
server {
    listen 443 ssl;
    add_header X-Frame-Options "DENY" always;
    add_header Content-Security-Policy "frame-ancestors 'none';" always;
}
# Flask — X-Frame-Options via after_request
@app.after_request
def set_security_headers(response):
    response.headers['X-Frame-Options'] = 'DENY'
    return response

Check 6: Missing Referrer-Policy

CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor) | A01:2021 - Broken Access Control | Severity: Medium

WHY: Without a Referrer-Policy, browsers send the full URL of the originating page in the Referer header when navigating to external sites. This

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.