From 40fa250a371172d3bd7342e71c3c58ef502b786f Mon Sep 17 00:00:00 2001 From: Kelly Michels Date: Mon, 31 Aug 2026 17:51:23 -0500 Subject: [PATCH] refactor(tests): move the sanitization denylist to a data file both sides can read (#66) The rules lived inside Sanitization.Tests.ps1, so the publisher in the private toolkit kept its own second list -- and the two guarded different things. The publisher's was about SECRETS: keys, private-key blocks, ssh targets. These 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). Proven at the time by copying the private ZHelpers.ps1 in: two failures naming EvoCivilCode, EvoPlatform and three private-only script names, against a scan that called the same file clean. tests/sanitization-patterns.psd1 is now the one source. The suite reads it and refuses to run if it is missing or empty, rather than passing vacuously against no rules -- an empty denylist that reports success is the failure this whole fix is about. No rule changed. Only where they live. .psd1 is not in the scanned extension list, which is deliberate and matches why Sanitization.Tests.ps1 excludes itself: a file that necessarily contains every pattern it looks for cannot also be scanned for them. Pester: 240 passed, 0 failed. CHECKSUMS regenerated; changelog and its plain-text twin updated. --- CHANGELOG.md | 10 +++++++ CHANGELOG.txt | 10 +++++++ tests/Sanitization.Tests.ps1 | 37 +++++++++---------------- tests/sanitization-patterns.psd1 | 46 ++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 25 deletions(-) create mode 100644 tests/sanitization-patterns.psd1 diff --git a/CHANGELOG.md b/CHANGELOG.md index fa41a33..8fb51d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,16 @@ Notable changes to the Evomedia.net Token Savers. ## Unreleased +### Changed +- **The sanitization denylist moved to `tests/sanitization-patterns.psd1`**, + so the test suite here and the publisher in the private toolkit read one + list instead of keeping two. They had two, and they disagreed: the + publisher's scan looked only for secrets, while these rules are about + identity — internal project names, product domains, private-only script + names, operator paths. It therefore reported "clean" on files this suite + rejects. No rule changed; only where they live. + + ### Changed - **`zdeploy` verification picks its channel on every retry, and never asks the bare IP** — the check used to choose its channel once, before the wait diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 49b5c99..f870e39 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -9,6 +9,16 @@ Notable changes to the Evomedia.net Token Savers. Unreleased ---------- +Changed +- The sanitization denylist moved to tests/sanitization-patterns.psd1, so + the test suite here and the publisher in the private toolkit read one + list instead of keeping two. They had two, and they disagreed: the + publisher's scan looked only for secrets, while these rules are about + identity - internal project names, product domains, private-only script + names, operator paths. It therefore reported "clean" on files this suite + rejects. No rule changed; only where they live. + + Changed - zdeploy verification picks its channel on every retry, and never asks the bare IP - the check used to choose its channel once, before the wait diff --git a/tests/Sanitization.Tests.ps1 b/tests/Sanitization.Tests.ps1 index 111e99f..a29552e 100644 --- a/tests/Sanitization.Tests.ps1 +++ b/tests/Sanitization.Tests.ps1 @@ -40,31 +40,18 @@ BeforeAll { } ) - # name -> what it is, so a failure explains itself - # pattern -> regex, case-insensitive - # Kept narrow on purpose: "evomedia.net" alone is legitimate here (the - # attribution header and the repo URL), so only the drive path and specific - # internal hosts are matched. - $script:Denied = @( - @{ Name = 'private project name'; Pattern = '\b(EvoCivilCode|EvoPlatform|DocketMail|SmartPlant\w*|ProvenSheet|evoehs|evoproven|evoaicc|evolocate|evoplatform)\b' } - @{ Name = 'private product domain'; Pattern = '\b(smartplantehs\.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|zmerge|zpull|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 = '(? +@{ + Denied = @( + @{ Name = 'private project name'; Pattern = '\b(EvoCivilCode|EvoPlatform|DocketMail|SmartPlant\w*|ProvenSheet|evoehs|evoproven|evoaicc|evolocate|evoplatform)\b' } + @{ Name = 'private product domain'; Pattern = '\b(smartplantehs\.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|zmerge|zpull|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 = '(?