# Pci Dss Audit

> Use when auditing code for PCI-DSS v4.0 compliance, reviewing cardholder data handling, checking credit-card storage and transmission, hunting PAN logging, or answering \"is this code PCI-compliant?\".

- **Type:** Skill
- **Install:** `agentstack add skill-kalshamsi-claude-security-skills-pci-dss-audit`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [kalshamsi](https://agentstack.voostack.com/s/kalshamsi)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [kalshamsi](https://github.com/kalshamsi)
- **Source:** https://github.com/kalshamsi/claude-security-skills/tree/main/skills/pci-dss-audit

## Install

```sh
agentstack add skill-kalshamsi-claude-security-skills-pci-dss-audit
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# PCI-DSS Audit

This skill performs static code analysis for PCI-DSS v4.0 compliance violations across JavaScript/TypeScript, Python, Java, Go, and C#/.NET projects. It identifies 12 common PCI-DSS code-level anti-patterns — unprotected cardholder data, weak encryption of PANs, missing audit trails, insufficient access controls, and more — mapping each finding to CWE and PCI-DSS v4.0 requirement numbers with concrete UNSAFE/SAFE code pairs for remediation.

**Scope:** This skill covers application-code-level controls only. It does NOT audit infrastructure, network segmentation, physical security, or organizational policies — those require separate assessment tools and processes.

## When to Use

- When the user asks to "audit PCI compliance", "check PCI-DSS", or "review payment card handling"
- When the user mentions "PCI audit", "cardholder data", "PAN protection", or "payment security"
- When scanning code that handles credit card numbers, CVVs, expiration dates, or payment tokens
- When reviewing code that stores, processes, or transmits cardholder data
- When a pull request modifies payment processing, card storage, or checkout flows
- When the user asks about "card data in logs", "PAN masking", "payment encryption", or "audit logging"
- When preparing for a PCI-DSS v4.0 Self-Assessment Questionnaire (SAQ) or Report on Compliance (RoC)

## When NOT to Use

- When the user is asking about network segmentation, firewall rules, or physical security (PCI-DSS Req 1, 9)
- When the user wants to audit infrastructure configurations (use `iac-scanner`)
- When the user needs general cryptographic review not related to payment data (use `crypto-audit`)
- When reviewing code that does not handle cardholder data or payment flows
- When the user needs a runtime scan of a live payment environment (use a DAST or ASV tool)
- When the user asks about **Dockerfiles, container security, or network segmentation** — you **MUST** decline and recommend `docker-scout-scanner` or `iac-scanner`
- When the user asks about **general cryptographic review** not related to payment data — you **MUST** decline and recommend `crypto-audit`

## Prerequisites

### Tool Installed (Preferred)

No external tool required. This skill uses code analysis only.

All 12 checks are performed through pattern matching and code inspection — no CLI tool needs to be installed, configured, or invoked.

### 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 analysis.

## Workflow

> **MANDATORY FIRST ACTION — Verify PCI-DSS scope before activating.**
>
> PCI-DSS applies only to code that handles cardholder data. Before starting the audit, grep the target for at least one of these signals:
>
> - File or directory names containing `payment`, `checkout`, `card`, `billing`, `transaction`, `stripe`, `braintree`, `adyen`, `square`, `paypal`, `merchant`, or `pci`
> - Imports of a payment SDK (`stripe`, `braintree`, `adyen`, `paypal`, `square`)
> - References to `cardNumber`, `pan`, `cvv`, `cvc`, `expiry`, `credit_card`, `card_number`, `payment_method`, `invoice`
> - Test fixtures explicitly scoped to PCI-DSS or cardholder data
>
> If none of these appear in the target, the target is outside PCI-DSS scope. Decline and redirect:
>
> > *"This code does not appear to handle cardholder data or payment flows — PCI-DSS does not apply. For general cryptographic review, use `crypto-audit`. For generic SAST, use `bandit-sast` or `security-review`."*
>
> A file of general cryptographic utilities (key management, hashing, session generation) is not in PCI-DSS scope just because it uses crypto primitives — that is the `crypto-audit` skill's domain. PCI-DSS findings require *cardholder data* to actually be present in the code. Framing generic crypto code as a "payment gateway SDK" to justify activation produces findings that do not describe what the code actually does and misleads the user about their compliance posture.

1. **Detect project languages** — Inspect project files to determine which languages are in use: `package.json` or `*.ts`/`*.js` (JavaScript/TypeScript), `requirements.txt`/`*.py` (Python), `pom.xml`/`*.java` (Java), `go.mod`/`*.go` (Go), `*.csproj`/`*.cs` (C#/.NET).
2. **Identify payment-relevant files** — Search for files that reference cardholder data or payment processing:
   - Filenames containing: `payment`, `checkout`, `card`, `billing`, `transaction`, `stripe`, `braintree`, `adyen`
   - Code importing payment SDKs: `stripe`, `braintree`, `adyen`, `paypal`, `square`
   - Code referencing card patterns: `cardNumber`, `pan`, `cvv`, `cvc`, `expiry`, `credit_card`, `card_number`
   - Code referencing encryption/tokenization: `encrypt`, `tokenize`, `vault`, `mask`
3. **Run the 12 PCI-DSS code checks** against each identified file (see Checks section below).
4. **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 PCI-DSS v4.0 requirement number
   d. Record file path and line number
   e. Generate the UNSAFE pattern found and the corresponding SAFE fix
   f. Draft a remediation recommendation
5. **Deduplicate and sort** findings by severity: Critical > High > Medium > Low.
6. **Generate the findings report** using the Findings Format below.
7. **Summarize** — State total findings, breakdown by severity, and top 3 remediation priorities.

## Checks

### Check 1: PAN Logged in Plaintext

**CWE-532** (Insertion of Sensitive Information into Log File) | **PCI-DSS Req 3.4, 10.3** | Severity: **Critical**

**WHY:** PCI-DSS Req 3.4 requires PANs to be rendered unreadable anywhere they are stored — including log files. Logging full card numbers creates an uncontrolled copy of cardholder data that persists in log aggregators, monitoring systems, and backups. Attackers who gain access to logs obtain card numbers without needing to breach the payment database.

**UNSAFE:**

```javascript
// JavaScript — full PAN in console log
function processPayment(cardNumber, amount) {
  console.log('Processing payment for card: ' + cardNumber);
  console.log(`Transaction: card=${cardNumber}, amount=${amount}`);
}
```

```python
# Python — card number in logging output
import logging
logger = logging.getLogger(__name__)

def charge_card(card_number, amount):
    logger.info(f"Charging card {card_number} for ${amount}")
```

```java
// Java — PAN in log statement
import org.slf4j.Logger;
public void processPayment(String cardNumber, double amount) {
    logger.info("Processing card: " + cardNumber);
}
```

**SAFE:**

```javascript
// Mask PAN to show only last 4 digits
function maskPan(cardNumber) {
  return '****-****-****-' + cardNumber.slice(-4);
}
function processPayment(cardNumber, amount) {
  console.log('Processing payment for card: ' + maskPan(cardNumber));
}
```

```python
# Mask PAN before logging
def mask_pan(card_number: str) -> str:
    return f"****-****-****-{card_number[-4:]}"

def charge_card(card_number, amount):
    logger.info(f"Charging card {mask_pan(card_number)} for ${amount}")
```

---

### Check 2: Cardholder Data in URL Parameters

**CWE-598** (Use of GET Request Method With Sensitive Query Strings) | **PCI-DSS Req 4.2** | Severity: **Critical**

**WHY:** Card data in URLs is recorded in browser history, web server access logs, proxy logs, and referrer headers. PCI-DSS Req 4.2 prohibits sending unprotected PANs via end-user messaging technologies, and URL parameters are inherently logged and cached by infrastructure outside your control.

**UNSAFE:**

```javascript
// JavaScript — card data in query string
const url = `/api/payment?cardNumber=${cardNumber}&cvv=${cvv}&expiry=${expiry}`;
fetch(url);
```

```python
# Python — card data as GET parameters
import requests
response = requests.get(
    f"https://api.example.com/charge?card={card_number}&cvv={cvv}"
)
```

```java
// Java — card in URL path
String url = "https://api.example.com/pay?pan=" + cardNumber + "&cvv=" + cvv;
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
```

**SAFE:**

```javascript
// Send card data in POST body over HTTPS
const response = await fetch('/api/payment', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ cardToken: tokenizedCard, amount }),
});
```

```python
# Use POST with tokenized card data
response = requests.post(
    "https://api.example.com/charge",
    json={"card_token": token, "amount": amount},
)
```

---

### Check 3: Weak Encryption of Stored Card Data

**CWE-327** (Use of a Broken or Risky Cryptographic Algorithm) | **PCI-DSS Req 3.5** | Severity: **Critical**

**WHY:** PCI-DSS Req 3.5 requires strong cryptography to protect stored PANs. MD5, Base64 encoding, SHA1, simple XOR, or reversible encoding are not encryption — they provide no meaningful protection. Attackers can trivially reverse Base64 or crack MD5/SHA1 hashes of 16-digit card numbers.

**UNSAFE:**

```javascript
// JavaScript — Base64 "encryption" of card number
function storeCard(cardNumber) {
  const encoded = Buffer.from(cardNumber).toString('base64');
  db.save({ encryptedCard: encoded });
}
```

```python
# Python — MD5 hash of card number (reversible via rainbow tables for 16-digit space)
import hashlib
card_hash = hashlib.md5(card_number.encode()).hexdigest()
```

```java
// Java — XOR "encryption"
byte[] encrypted = new byte[cardNumber.length()];
for (int i = 0; i = 0; i--) {
    let n = parseInt(num[i], 10);
    if (alternate) { n *= 2; if (n > 9) n -= 9; }
    sum += n;
    alternate = !alternate;
  }
  return sum % 10 === 0;
}

function processPayment(cardNumber, cvv, expiry, amount) {
  const sanitized = cardNumber.replace(/[\s-]/g, '');
  if (!/^\d{13,19}$/.test(sanitized)) throw new Error('Invalid card number format');
  if (!luhnCheck(sanitized)) throw new Error('Card number fails Luhn check');
  if (!/^\d{3,4}$/.test(cvv)) throw new Error('Invalid CVV format');
  if (typeof amount !== 'number' || amount  bool:
    digits = [int(d) for d in card_number]
    odd_digits = digits[-1::-2]
    even_digits = digits[-2::-2]
    total = sum(odd_digits) + sum(d * 2 - 9 if d * 2 > 9 else d * 2 for d in even_digits)
    return total % 10 == 0

def charge(card_number: str, cvv: str, amount: float):
    sanitized = card_number.replace(" ", "").replace("-", "")
    if not sanitized.isdigit() or not (13  {
  const result = await gateway.charge(req.body.amount, req.body.cardToken);
  res.json(result);
});

app.post('/api/payments/refund', async (req, res) => {
  const result = await gateway.refund(req.body.transactionId, req.body.amount);
  res.json(result);
});
```

```python
# Python Flask — no authentication
@app.route('/api/payments/charge', methods=['POST'])
def charge():
    data = request.get_json()
    result = gateway.charge(data['amount'], data['card_token'])
    return jsonify(result)
```

```java
// Java Spring — no security annotation
@RestController
public class PaymentController {
    @PostMapping("/api/payments/charge")
    public ResponseEntity charge(@RequestBody PaymentRequest request) {
        return ResponseEntity.ok(paymentService.charge(request));
    }
}
```

**SAFE:**

```javascript
// Require authentication and authorization
const { authenticate, authorize } = require('./middleware/auth');

app.post('/api/payments/charge',
  authenticate,
  authorize('payments:charge'),
  async (req, res) => {
    const result = await gateway.charge(req.body.amount, req.body.cardToken);
    res.json(result);
  }
);
```

```python
# Flask with authentication required
from functools import wraps

@app.route('/api/payments/charge', methods=['POST'])
@login_required
@require_permission('payments:charge')
def charge():
    data = request.get_json()
    result = gateway.charge(data['amount'], data['card_token'])
    return jsonify(result)
```

```java
// Spring Security with role-based access
@RestController
@PreAuthorize("hasRole('PAYMENT_PROCESSOR')")
public class PaymentController {
    @PostMapping("/api/payments/charge")
    @PreAuthorize("hasAuthority('PAYMENT_CHARGE')")
    public ResponseEntity charge(@RequestBody PaymentRequest request) {
        return ResponseEntity.ok(paymentService.charge(request));
    }
}
```

---

### Check 10: Card Numbers in Error Messages

**CWE-209** (Generation of Error Message Containing Sensitive Information) | **PCI-DSS Req 3.4, 6.2** | Severity: **High**

**WHY:** Error messages containing card numbers leak cardholder data to end users, client-side logs, error tracking services (Sentry, Datadog), and browser consoles. PCI-DSS requires PANs to be masked everywhere they appear, and Req 6.2 requires secure error handling that does not expose sensitive data.

**UNSAFE:**

```javascript
// JavaScript — card number in error response
function validateCard(cardNumber) {
  if (!isValidCard(cardNumber)) {
    throw new Error(`Invalid card number: ${cardNumber}`);
  }
}
```

```python
# Python — card data in exception
def process_payment(card_number, amount):
    try:
        gateway.charge(card_number, amount)
    except GatewayError as e:
        raise PaymentError(f"Failed to charge card {card_number}: {e}")
```

```java
// Java — card number in error message
public void validate(String cardNumber) {
    if (!isValid(cardNumber)) {
        throw new IllegalArgumentException("Invalid card: " + cardNumber);
    }
}
```

**SAFE:**

```javascript
// Never include card data in errors — use masked or reference IDs
function validateCard(cardNumber) {
  if (!isValidCard(cardNumber)) {
    throw new Error(`Invalid card number ending in ${cardNumber.slice(-4)}`);
  }
}
```

```python
# Mask card data in exceptions
def process_payment(card_number, amount):
    try:
        gateway.charge(card_number, amount)
    except GatewayError as e:
        masked = f"****{card_number[-4:]}"
        raise PaymentError(f"Failed to charge card {masked}: {e}")
```

---

### Check 11: CVV/CVC Stored Post-Authorization

**CWE-257** (Storing Passwords in a Recoverable Format) | **PCI-DSS Req 3.3.2** | Severity: **Critical**

**WHY:** PCI-DSS Req 3.3.2 explicitly prohibits storing the card verification code (CVV/CVC) after authorization. There is no legitimate reason to retain this value. Storing it — even encrypted — is a direct PCI-DSS violation that can result in immediate non-compliance and significant fines.

**UNSAFE:**

```javascript
// JavaScript — saving CVV to database
async function savePaymentMethod(userId, cardNumber, cvv, expiry) {
  await db.query(
    'INSERT INTO payment_methods (user_id, card_number, cvv, expiry) VALUES (?, ?, ?, ?)',
    [userId, cardNumber, cvv, expiry]
  );
}
```

```python
# Python — CVV persisted in model
class PaymentMethod(db.Model):
    card_number = db.Column(db.String(19))
    cvv = db.Column(db.String(4))  # NEVER store CVV
    expiry = db.Column(db.String(7))
```

```java
// Java — CVV in entity field
@Entity
public class PaymentMethod {
    @Column(name = "cvv")
    private String cvv;  // PCI-DSS violation — CVV must never be stored
}
```

**SAFE:**

```javascript
// Use CVV only during authorization, never persist it
async function authorizePayment(cardNumber, cvv, expiry, amount) {
  // CVV used only for this authorization call
  const authResult = await gateway.authorize({ cardNumber, cvv, expiry, amount });

  // Store only the token — NEVER the CVV
  await db.query(
    'INSERT INTO payment_methods (user_id, card_token, last_four) VALUES (?, ?, ?)',
    [userId, authResult.token, cardNumber.slice(-4)]
  );
  return authResult;
}
```

```python
# CVV used in-memory only, never persisted
class PaymentMethod(db.Model):
    card_token = db.Column(db.String(64))   # Tokenized reference
    last_four = db.Column(db.String(4))      # Display purposes only
    # NO cvv column — CVV is never stored

…

## Source & license

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

- **Author:** [kalshamsi](https://github.com/kalshamsi)
- **Source:** [kalshamsi/claude-security-skills](https://github.com/kalshamsi/claude-security-skills)
- **License:** MIT

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

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-kalshamsi-claude-security-skills-pci-dss-audit
- Seller: https://agentstack.voostack.com/s/kalshamsi
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
