zscripts-token-savers/tests/sanitization-patterns.psd1
Kelly Michels 193d6d86a1
chore(tests): stop the denylist publishing the retired name it guards (#82)
This repo is public, and the denylist that keeps private identifiers out of it
spelled two of them in full: the retired EHS product name, and its old domain.
The file protecting the name was the file publishing it.

Deleting those two rules was not an option. The private tree still carries that
name in ~20 places - sp_fix_kelly_email_prod.ps1 alone has the old domain and a
real prod stack path - so both rules are live, not stale. Dropping them would
trade a visible string for an actual leak path.

So split the literal with a one-character class instead: Smart[P]lant and
smart[p]lantehs. A class of one matches exactly that character, so the regex is
unchanged - verified by matching both spellings and the old domain before and
after, with negative controls - while the contiguous string no longer appears
in a public file.

Commented in place, because the obvious "tidy-up" is to un-split it.

Pester: tests/Sanitization.Tests.ps1 14 passed, 0 failed.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 13:22:20 -05:00

59 lines
3.9 KiB
PowerShell

<#
Patterns the PUBLIC repo must never contain.
Extracted from Sanitization.Tests.ps1 so that the test here and the
publisher in the private tree read ONE list instead of keeping two.
They had two, and they disagreed: the publisher's scan looked only for
secrets - keys, private-key blocks, ssh targets - while these rules are
about IDENTITY: internal project names, product domains, private-only
script names, operator paths.
So the publisher reported "clean" on files this suite rejects, and would
have published a tree that fails the public repo's own tests
(evo.scripts#106). One list, and that cannot drift apart again.
A denylist proves the absence of KNOWN patterns, not the absence of
secrets. It is a regression net for a specific recurring mistake, not a
substitute for reading what you publish. Add a pattern whenever a new
private identifier appears; a stale entry costs nothing.
Kept narrow on purpose: "evomedia.net" alone is legitimate here - the
attribution header and the repo URL both carry it - so only the drive
path and specific internal hosts are matched.
#>
@{
Denied = @(
# The retired EHS name is split with a one-character class in both rules
# below (Smart[P]lant, smart[p]lantehs). The regex is identical - a class of
# one matches exactly that character - but the literal no longer appears in
# this file, which is public. The private tree still carries that name in
# ~20 places, so these rules are still load-bearing: do not delete them,
# and do not un-split them.
@{ Name = 'private project name'; Pattern = '\b(EvoCivilCode|EvoPlatform|DocketMail|Smart[P]lant\w*|ProvenSheet|evoehs|evoproven|evoaicc|evolocate|evoplatform)\b' }
# The current spellings, which the line above never saw: a dot or a
# hyphen breaks the word and an underscore hides the boundary, so
# evo.ehs, evo-ai and evoehs_app all passed (evo.scripts#138 in the
# private tree). Internal issue references travel with them.
@{ Name = 'current product name'; Pattern = '(?i)\bevo[.-](ehs|ai|edge|locate|proven|platform|civilcode|scripts)\b|\bevoehs' }
@{ Name = 'internal issue reference'; Pattern = 'evo\.scripts#\d+' }
@{ Name = 'private product domain'; Pattern = '\b(smart[p]lantehs\.com|provensheet\.com|evoehs\.com|civilcode\.evomedia\.net|dashboard\.evomedia\.net|webmail\.evomedia\.net|mail-admin\.evomedia\.net|docketmail\.evomedia\.net|cardiff\.evomedia\.net|platform\.evomedia\.net|ai\.evomedia\.net|git\.evomedia\.net|analytics\.evomedia\.net)\b' }
@{ Name = 'private-only script'; Pattern = '\b(register_civilcode|register_docketmail|sp_seed_demo_prod|zpublish_stats|zcoverage|zresume|swag_set_owner|provision_demo|apply_platform_config_fixes)\b' }
@{ Name = 'local drive path'; Pattern = '[A-Za-z]:\\\\?evomedia\.net' }
@{ Name = 'operator home path'; Pattern = '/home/ubuntu/' }
@{ Name = 'real pem key name'; Pattern = 'evomedia-prod\.pem' }
# RFC 5737 reserves 203.0.113.0/24 for documentation - that one is the
# correct placeholder and must stay allowed, as are loopback and the
# private ranges. Anything else that looks like a public IPv4 literal is
# suspect.
#
# The boundaries are [\d.] rather than \d on purpose: this toolkit's own
# 5-segment version (v1.0.0.0.14) contains "0.0.0.14", which a plain
# digit boundary happily reads as an address. Refusing a match that
# touches another dot rules out every version string without weakening
# detection of a real address, which is always delimited by whitespace
# or quotes.
@{ Name = 'non-documentation IP'; Pattern = '(?<![\d.])(?!203\.0\.113\.)(?!127\.0\.0\.1)(?!0\.0\.0\.0)(?!255\.)(?!10\.)(?!192\.168\.)(?!172\.(1[6-9]|2\d|3[01])\.)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?![\d.])' }
)
}