cybersecurity · intermediate · ~30 min

Redact credit-card-like number runs

Combine character-class scanning with a numeric checksum.

Challenge

Mask credit-card numbers in a log line — the PCI-DSS rule of thumb is: any run of 13-19 digits that passes the Luhn checksum is treated as a card number.

Task

Implement void redact_cards(char *line) that mutates line in place, replacing every run of plain digits whose length is in [13, 19] AND that passes the Luhn checksum with the literal text [redacted].

The Luhn check:

  1. Walk the digits right to left.
  2. Double every second digit; if the result is > 9, subtract 9.
  3. Sum all the (possibly adjusted) digits.
  4. The number passes if the total mod 10 is 0.

Input

  • line: a NUL-terminated, writable buffer. The grader passes a fixed line. Only runs of plain digits count — spaces or dashes break a run.

Output

No return value. line is rewritten in place with every matching card-number run replaced by [redacted].

Example

"card 4532015112830366 in log"          ->   "card [redacted] in log"   (Luhn-valid, 16 digits)
"old 1234567890123456 is invalid"       ->   "old 1234567890123456 is invalid"   (Luhn-invalid)
"ten digits 1234567890 leave alone"     ->   unchanged   (only 10 digits)
"long 12345678901234567890 leave alone" ->   unchanged   (20 digits, too long)
"two 4532015112830366 then 79927398713 ok" ->   "two [redacted] then 79927398713 ok"   (2nd run is 11 digits)

Edge cases

  • Digit run shorter than 13 or longer than 19: leave it.
  • Run in the length window but Luhn-invalid: leave it.
  • Multiple cards on one line: redact each.

Rules

  • Rewrite in place; no allocation beyond a local scratch buffer.

Why this matters

PCI-DSS compliance requires masking primary account numbers (PANs) in logs and exports. The pragmatic check is: any run of 13–19 digits that passes the Luhn checksum is treated as a card number and redacted.

Input format

A NUL-terminated, writable line with digit runs to scan.

Output format

No return value; line is mutated so every Luhn-valid 13-19 digit run becomes [redacted].

Constraints

Only plain-digit runs of length 13-19 that pass Luhn are redacted; rewrite in place.

Starter code

void redact_cards(char *line) { /* TODO */ }

Common mistakes

Forgetting the length window (13..19). Doubling the wrong digit (it's every second from the right, not the left).

Edge cases to handle

Digit run too short; too long; valid Luhn but not in window; multiple cards in one line.

Complexity

O(strlen(line) * avg-digit-run-length).

Background lessons

Up next

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.