Testland
Browse all skills & agents

pci-dss-control-test-author

Build-an-X for PCI DSS v4.0 scope verification - cardholder data environment (CDE) boundary tests, segmentation tests (PCI Req 1), prohibited-data-storage assertions per Req 3 (no full track data, no CVV/CAV2/CVC2/CID, no PIN/PIN block post-authorization), key-management tests per Req 3.6, encryption-of-transmissions per Req 4; includes the scope catalog (SAQ A / A-EP / D levels, PAN-storage rules, hosted-fields / tokenization scope-reduction patterns) in references/pci-scope.md. Use when authoring PCI DSS scope-reduction + control tests for any system handling payment-card data, or when determining a payment integration's SAQ level.

Install with skills.sh (any agent)

npx skills add testland/qa --skill pci-dss-control-test-author
View source

pci-dss-control-test-author

This is a build-an-X workflow for verifying CDE scope is correctly bounded and prohibited-data assertions are captured in fixtures, targeting PCI DSS v4.0 (fully required March 2025).

Helper stubs

Key interfaces used across steps (provided by the test harness):

# network / infra
def network_policy.get_allowed_connections(src: str, dst: str) -> list[str]: ...

# data-access
def export_database() -> DatabaseDump: ...
def recent_logs() -> list[LogEntry]: ...
def scan_repo_files() -> list[str]: ...
def read_file(path: str) -> str: ...

# TLS inspection - TLSInfo has: .protocol (str e.g. 'TLSv1.2'), .cipher_strength (int), .cipher (str)
def inspect_tls(endpoint: str) -> TLSInfo: ...

# crypto / format
def is_encrypted_aes_256(value: str) -> bool: ...
def is_truncated_last_4(value: str) -> bool: ...
def is_hashed_with_salt(value: str) -> bool: ...
def is_tokenized(value: str) -> bool: ...
def redact(value: str) -> str: ...

# constants
CDE_API_ENDPOINTS: list[str] = [...]   # All API endpoints inside the CDE
DEPRECATED_CIPHERS: list[str] = [...]  # e.g. ['RC4', 'DES', '3DES', 'NULL']

Step 1 - Define + assert CDE boundary

# pci_scope.py
# untested CDE/non-CDE network policy drifts into a segmentation breach
CDE_SYSTEMS = {
    'payment-service',
    'tokenization-service',
    'card-vault-db',
    'pci-zone-fw',
}
NON_CDE_SYSTEMS = {
    'web-frontend',     # tokens only; no raw PAN
    'analytics',         # never sees PAN
    'support-tickets',   # never sees PAN
}

def test_cde_systems_isolated_from_non_cde():
    for cde_system in CDE_SYSTEMS:
        for non_cde in NON_CDE_SYSTEMS:
            # Network policy MUST block direct connections from non-CDE to CDE
            allowed = network_policy.get_allowed_connections(cde_system, non_cde)
            # Only via tokenization gateway (via PCI zone FW)
            assert allowed == [] or allowed == ['via-pci-zone-fw']

Segmentation testing per PCI DSS Req 11.4.1 must be performed at least every 6 months by a qualified internal resource OR external penetration tester.

Checkpoint: If any CDE/non-CDE boundary assertion fails, halt and remediate the network policy before proceeding. A segmentation failure means downstream steps may be testing a mis-scoped environment.

Step 2 - Assert no SAD storage post-authorization (Req 3.2)

SAD may transit during auth but storage after auth completes (logs, DB, audit trails, backup) is forbidden - full track data, CVV2/CVC2/CID, and PIN/PIN block, all never post-authorization.

import re

# PAN slips into a log line if you trust developers never to log it
TRACK1_PATTERN = re.compile(r'%[A-Z]\d{12,19}\^[^\?]*\?\d*\?')   # Track 1
CVV_PATTERN    = re.compile(r'(?<!\d)\d{3,4}(?!\d)')               # naive; pair with DLP

def test_no_full_track_data_in_storage():
    """Req 3.2.1: track data must not be retained post-authorization."""
    db_dump = export_database()
    for table in db_dump.tables:
        for row in table:
            for value in row.values():
                assert not TRACK1_PATTERN.search(str(value)), \
                    f"Full track data in {table.name}"

def test_no_cvv_in_logs():
    """Req 3.2.2: CVV2/CVC2/CID must not be retained."""
    log_entries = recent_logs()
    for entry in log_entries:
        # Look for proximity of CVV-like 3-4 digit numbers near "cvv" / "card" tokens
        if 'cvv' in entry.text.lower() or 'card' in entry.text.lower():
            matches = CVV_PATTERN.findall(entry.text)
            for m in matches:
                assert m == '***' or m == '----', \
                    f"Possible CVV in log: {entry.id}"

Checkpoint: If SAD is found in storage or logs, halt and remediate before proceeding to Step 3. Continuing to test encryption-at-rest while SAD is present misrepresents compliance posture. File a severity-1 finding and trigger your incident response process before re-running.

Step 3 - Assert PAN encryption at rest (Req 3.4) + key management (Req 3.6)

# hardcoded encryption keys in the repo are a Req 3.6 violation and expose PAN

def test_pan_stored_encrypted():
    """Req 3.4: PAN unreadable wherever stored (encryption / truncation / hashing / tokenization).
    Prefer tokenization for new paths; treat the others as escape hatches for pre-existing systems.
    """
    card_record = CardVault.objects.first()
    raw_pan = card_record._raw_pan_field    # accessor for storage-format field

    # Format MUST be one of:
    #  - encrypted (AES-256 or stronger)         <- preferred for new paths
    #  - truncated (e.g., last 4 only)
    #  - hashed (with salt; one-way)
    #  - tokenized (replaced with non-sensitive token)  <- default for new systems
    valid_format = (
        is_encrypted_aes_256(raw_pan)
        or is_truncated_last_4(raw_pan)
        or is_hashed_with_salt(raw_pan)
        or is_tokenized(raw_pan)
    )
    assert valid_format, f"PAN stored unprotected: {redact(raw_pan)}"

def test_decryption_keys_not_in_app_repo():
    """Req 3.6: cryptographic keys protected against unauthorized access."""
    repo_files = scan_repo_files()
    for f in repo_files:
        content = read_file(f)
        # No hardcoded AES keys (high entropy + length 256+ bits)
        assert not re.search(r'AES_KEY\s*=\s*["\'][A-Za-z0-9+/=]{40,}', content), \
            f"Possible hardcoded AES key in {f}"
        # No KMS key file references
        assert 'kms-private-key.pem' not in f, \
            f"KMS private key file referenced in repo: {f}"

Checkpoint: If PANs are stored unprotected or keys are found in the repo, halt. Key-in-repo findings require immediate secret rotation before continuing.

Step 4 - Encryption of transmissions (Req 4)

# TLS 1.0 / 1.1 still enabled is a Req 4 violation
def test_pan_only_transmitted_via_strong_crypto():
    """Req 4.2.1: strong crypto for transmission of cardholder data over open networks."""
    for endpoint in CDE_API_ENDPOINTS:
        tls_info = inspect_tls(endpoint)
        assert tls_info.protocol >= 'TLSv1.2', \
            f"Weak TLS protocol on {endpoint}: {tls_info.protocol}"
        assert tls_info.cipher_strength >= 256, \
            f"Cipher strength too low on {endpoint}: {tls_info.cipher_strength}"
        assert tls_info.cipher not in DEPRECATED_CIPHERS, \
            f"Deprecated cipher on {endpoint}: {tls_info.cipher}"

Checkpoint: TLS failures on any CDE endpoint are blocking. Deprecated protocols (TLS 1.0 / 1.1) must be disabled before proceeding to access-control testing.

Step 5 - Access control (Req 7 + 8)

# generic shared accounts (e.g. 'admin' / 'svc-account') violate Req 8.2.1
def test_cde_access_requires_unique_id():
    """Req 8.2.1: assign all users a unique ID before access to system components."""
    # No shared / generic accounts
    response = client.post('/cde-api/login', json={'username': 'shared-svc', 'password': 'secret'})
    assert response.status_code == 403

def test_cde_access_requires_mfa():
    """Req 8.4: implement MFA for all access into the CDE."""
    response = client.post('/cde-api/login', json={
        'username': 'alice@example.com',
        'password': correct_password,
        # No MFA token
    })
    assert response.status_code == 401
    assert response.json()['error'] == 'mfa_required'

Checkpoint: Shared-account or MFA bypass failures are blocking. Do not proceed to logging tests while unauthorized access paths remain open.

Step 6 - Logging (Req 10)

Cross-ref audit-trail-test-author:

def test_pan_access_creates_audit_record():
    """Req 10.2: audit trails to reconstruct events."""
    user.access_card(card_id=123)
    audit = AuditLog.objects.filter(
        actor=user.id,
        action='pan_access',
        subject=f'card:{card_id}',
    ).first()
    assert audit is not None
    assert audit.timestamp is not None
    # Audit log itself MUST not contain the PAN:
    assert not re.search(r'\d{13,19}', audit.full_event_text), \
        "PAN found in audit log text - log masking is broken"

Step 7 - Scope reduction strategies

PCI DSS scope reduction is the highest-leverage cost saving. Default: tokenization - replace the PAN with a non-sensitive token at the earliest possible boundary so downstream systems handle tokens only, which shrinks the CDE the most for the least integration churn. The alternatives (hosted iframe payment page, P2PE for card-present flows, network segmentation as layered defense only) and the Req 3.4 storage-format rationale are in references/strategies.md. The full scope catalog - SAQ level selection (A / A-EP / D), PAN-storage rules, per-gateway hosted-fields patterns, and the testable behaviours the scope boundary creates - is in references/pci-scope.md.

PAN-storage format default (Req 3.4): the four is_* checks in Step 3's test_pan_stored_encrypted are an OR because pre-existing systems may already use any of them; for new storage paths pick tokenization and treat encryption / truncation / hashing as escape hatches.

Limitations

  • Targets PCI DSS v4.0; v3.2.1 has subtle differences.
  • QSA audit-style validation requires a credentialed assessor; tests verify implementation only.
  • CVV regex patterns are approximate; pair with QSA-validated DLP tooling.
  • Scope-reduction strategies require organizational and technical changes beyond this skill.

References

PCI DSS scope catalog - SAQ levels, PAN-storage rules, scope-reduction patterns

View source (opens in new window)

PCI DSS scope catalog - SAQ levels, PAN-storage rules, scope-reduction patterns

Pure-reference catalog of PCI DSS v4.0 scope reduction techniques + the testable scope boundaries. This is the catalog of WHY the boundary matters and what it makes testable; the SKILL.md steps are the workflow that verifies a given integration against it.

Scope reduction is the dominant strategy: keep card data off your systems entirely, so PCI compliance becomes minimal SAQ A instead of full SAQ D.

How to use this catalog

  1. Identify your integration pattern - review the scope-reduction patterns below (hosted fields, redirect, tokenization, network segmentation) and match the one your payment integration uses.
  2. Determine your SAQ level - use the SAQ table to confirm which Self-Assessment Questionnaire level applies given how cardholder data flows through your system.
  3. Verify testable behaviours - cross-reference the Testable behaviours table and anti-patterns, then run the SKILL.md steps against the boundary.

SAQ levels (Self-Assessment Questionnaire)

Per pcisecuritystandards.org (opens in new window):

SAQDescriptionScope
ACard-not-present, fully outsourced (hosted gateway pages, iFrame redirects, Stripe Elements)Smallest - your servers never see PAN
A-EPHosted-form-with-merchant-customisation (e.g., your domain shows the form but iframe is the gateway's)Slightly larger; some elements visible to your server
DAll merchants not covered by A-C; full PCI DSSLargest - for cases where you must handle PAN

Choose A when feasible: PAN never touches your servers because the customer inputs it directly into a gateway-hosted iframe / element.

PAN storage rules

Per PCI DSS v4.0 §3.4: prohibited storage of:

  • Full PAN cleartext anywhere
  • Sensitive authentication data (full track, CVV/CVC, PIN/PIN block) post-authorisation
  • More than first-6 + last-4 digits in any retained data (truncated)

Allowed:

  • First-6 + last-4 digits (truncated PAN)
  • Tokens issued by the gateway (e.g., Stripe pm_*)
  • Encrypted PAN with strong key management (if you must store full PAN)

Tests for storage (PostgreSQL ~ regex operator):

-- Detect prohibited PAN patterns in any string column
SELECT * FROM <any_table>
WHERE  column ~ '^[0-9]{13,19}$'
    OR column ~ '^4[0-9]{15}$'
    OR column ~ '^5[1-5][0-9]{14}$'
LIMIT 10;
-- Expect: 0 rows

Scope-reduction patterns

1. Hosted fields / Elements

Per stripe.com/docs/payments/payment-element (opens in new window), docs.adyen.com/payment-methods/cards/web-drop-in (opens in new window), developer.paypal.com/braintree/docs/start/hosted-fields (opens in new window):

<!-- Stripe Element -->
<form>
  <div id="payment-element"></div>   <!-- iframe; PAN stays in Stripe's iframe -->
  <button>Pay</button>
</form>

PAN never reaches your JS or backend. The Element sends to Stripe directly; your server gets a token.

2. Redirect-to-gateway

Customer redirects to gateway-hosted page; pays; redirects back with a token / transaction ID.

PCI-friendly because PAN never on your domain. UX-tradeoff: slower, less branded.

3. Tokenization API

Backend-to-backend: customer submits PAN to gateway directly (via JS); gateway returns token; your code uses token.

Variants per gateway: Stripe setupIntent for saved cards; Adyen paymentMethods.storeDetails; PayPal Vault.

4. Network segmentation

If you must touch PAN, isolate it in a separate network with strict ingress / egress + monitoring. Reduces scope of the broader IT environment.

Testable behaviours

BehaviourTest
No 16-digit numbers in DBSQL regex against all string columns
No CVV / CVC storedSearch code for cvc, cvv, cardholderVerification
Hosted fields render without exposing PAN to your JSBrowser DevTools Network tab - no PAN in requests to your origin
Webhooks contain tokens not PANParse webhook payloads; assert no 16-digit numbers
Log scrubbingTest logs for PAN patterns; should be redacted
Backup snapshots PAN-freeSame regex against backup files
Egress firewall blocks card-network IPsNetwork test

The SKILL.md steps run these adversarially; this catalog provides the rationale.

Anti-patterns

Anti-patternWhy it failsFix
Log entire payment request bodyPAN in logsScrub at log emit
Stage card-collection on your own pageCards now in your domain → SAQ DUse hosted fields
Send PAN to backend then forward to gatewayServer now PCI-scopeDirect JS-to-gateway
Store PAN encrypted "just in case"Key management is half of PCI DSSUse tokens
Test PAN in fixturesReal PAN in commitsUse only platform-provided test PANs
Capture CVV server-sidePCI DSS v4.0 §3.2.1: prohibited post-authDon't capture or capture in scoped iframe
Skip scope-checker in CIDrift over timePeriodic scope audit

Limitations

  • PCI DSS v4.0 is paywalled (cite by stable ID). Gateway docs paraphrase the relevant clauses.
  • Scope is a moving target. Adding a new feature can pull PAN into your system; re-audit on architecture changes.
  • PCI compliance ≠ PCI security. Compliance is a baseline; actual security needs more (threat modeling, pentest, etc.).
  • Doesn't cover PA-DSS (Payment Application DSS for POS software).

Sources

PCI DSS scope-reduction strategies

View source (opens in new window)

PCI DSS scope-reduction strategies

Companion to pci-dss-control-test-author/SKILL.md, Step 7. PCI DSS scope reduction is the highest-leverage cost saving: every system removed from the cardholder data environment (CDE) is a system you no longer have to assess.

Strategies, most effective first

  • Tokenization (default). Replace the PAN with a non-sensitive token at the earliest possible boundary so downstream systems handle tokens only. Shrinks the CDE the most for the least integration churn in a typical SaaS architecture.
  • Hosted payment page (iframe). Use when pre-tokenization is not feasible and card data must never touch your servers; the card fields are served by the payment provider inside an iframe, not by your application.
  • P2PE (point-to-point encryption). For physical POS / card-present flows; card data is encrypted at swipe with the only decryption point inside the CDE.
  • Network segmentation. A layered defense on top of one of the above, never the sole scope-reduction strategy - it isolates the CDE but does not remove data from it. Segmentation is validated per Req 11.4.1 at least every 6 months.

PAN-storage format default (Req 3.4)

Prefer tokenization at the storage boundary. The four is_* checks in Step 3's test_pan_stored_encrypted are an OR because pre-existing systems may already use encryption, truncation, or hashing, but for new storage paths pick tokenization and treat encryption / truncation / hashing as escape hatches for when tokenization is not feasible. This keeps the number of systems that ever hold recoverable PAN as small as possible, which is the whole point of scope reduction.

Related skills

audit-trail-test-author

Build-an-X for audit-log tests across compliance frameworks - required-events catalog (auth events / privilege change / data access / admin action / config change / export / impersonation); structured-log-format assertions per OWASP A09:2021; tamper-evident chain (hash-chain + signed-batch patterns) for HIPAA §164.312(b) + PCI Req 10 + SOC 2 CC7.3; immutability + retention per framework; query-replay tests for forensic reconstruction. Use when authoring audit log tests for any compliance framework (HIPAA / PCI / SOC 2 / GDPR / etc.).

compliance-coverage-scoring

Scores existing tests and evidence against a named compliance framework's criteria list (GDPR, CCPA/CPRA, SOC 2 Trust Services Criteria, HIPAA Security Rule, PCI DSS, ISO/IEC 27001), marking every criterion met, partial, not met, or not applicable with a stated evidence requirement per state, and recording each scope exclusion with its criterion reference, reason, named approver, and re-review date. Includes an adversarial readiness-review mode with hard refusal rules (never "ready" with an unjustified gap), and the ISO/IEC 27001:2022 Annex A per-control test-pattern catalog in references/iso27001.md. Produces a readiness self-assessment only: not certification, not an audit opinion, not legal advice. Use when a framework version has been named and an evidence set already exists, and someone needs a per-criterion readiness score before an observation period opens, before a qualified assessor arrives, or in response to a regulator inquiry.

gdpr-test-patterns

Reference catalog of GDPR-aligned test patterns - data-subject-rights workflows (Art. 15 access, Art. 16 rectification, Art. 17 erasure / "right to be forgotten", Art. 18 restriction, Art. 20 portability, Art. 21 objection); consent recording + revocation per Art. 7; data-residency assertions per Art. 44 - 50 international transfers; breach-notification timing tests per Art. 33 (72 hours); data-minimization assertions in fixtures per Art. 5(1)(c). The California analogue - CCPA/CPRA patterns by Cal. Civ. Code section, including Global Privacy Control (GPC) opt-out, right-to-know, deletion, right-to-correct, and sensitive-PI limits - lives in references/ccpa.md. Use when authoring GDPR- or CCPA/CPRA-readiness tests for any product processing EU or California personal data.

hipaa-test-patterns

Reference catalog of HIPAA Security Rule-aligned test patterns - administrative safeguards (45 CFR §164.308: workforce training, access management, contingency planning), physical safeguards (§164.310: facility access, workstation security, device disposal), technical safeguards (§164.312: access control, audit logs, integrity, transmission security); PHI handling assertions in fixtures; minimum-necessary tests per §164.502(b); BAA-scope boundary verification. Use when authoring HIPAA-readiness tests for any product handling Protected Health Information.

soc2-evidence-collector

Build-an-X for SOC 2 Type II evidence collection and auditor-facing packaging - per-Trust-Services-Criterion test artifacts (Common Criteria CC1.1 - CC9.2; plus Availability A1, Confidentiality C1, Processing Integrity PI1, Privacy P1 - P9 if in scope); auto-collection from CI logs + audit trails + access logs + change-management records; alignment with Vanta / Drata / Secureframe evidence shapes; observation-period sampling. Cross-framework evidence packaging (control-evidence matrix, timestamped bundles, chain-of-custody notes per NIST SP 800-72 - also for ISO 27001 / HIPAA / PCI DSS / GDPR / FedRAMP) lives in references/evidence-packaging.md. Use when the team is preparing for SOC 2 Type II audit and needs continuous evidence collection, or when any audit engagement requires auditor-ready evidence packages built from automated test output.