Changing a name and a hero photograph can make a website look new. The previous material may still remain in alternative text, sharing titles, lower-page notices or filenames. I treat replacement as a review of the public material, rather than ending when a few visible areas have changed.
The replacement engine I inspected generates public metadata from the new information and stops generation when known previous identifiers remain. This editorial system also separates private evidence from the public body and exposes reviewed media through current published references. I have turned those lessons into a small read-only checker. Its text checks do not claim to identify wording inside photographs.
Select the public output before scanning
A working directory may contain originals, private review notes, credentials and older versions. Treating all of them as public files misrepresents exposure. Checking only the manuscript can miss a title, sharing description or asset introduced during a build. I first distinguish the actual deployment file list from code sent only as a server bundle.
The module reads a public directory explicitly selected by its caller. It excludes server files only when the caller chooses the supported deployment contract that bundles them separately, as this site’s uploader does. It does not exclude arbitrary files merely because their names look familiar. Remote CMS bodies and media remain outside this static-directory scan and require their own API review.
Previous descriptions can remain beyond the headline
A large visible heading is relatively easy to replace. Alternative text, metadata, sharing titles, link attributes and paths can escape an ordinary visual review. The inspected replacement engine regenerates titles and sharing information from the new material and changes image descriptions, then checks the public HTML for known previous identifiers.
This smaller checker receives known markers as private inputs. It decodes HTML entities, normalizes Unicode and whitespace, and compares body text, attributes and filenames. It also examines wording split by tags and valid JSON string literals. It is not a universal detector for unknown identities or every obfuscation. Findings use marker indexes rather than repeating matching sentences; identity-bearing paths are represented by a digest.
A media hash asks whether reviewed bytes have changed
Renaming an image does not remove identifiers inside it. A sign in a photograph, the title area of a drawing, video captions or speech, and the last page of a document can carry previous information. The actual material and its permitted public use need direct review. A clean text scan is not sufficient evidence to approve visual media.
After that review, a SHA256 digest can record the exact approved file. A later mismatch asks for another review. A matching digest means the bytes are unchanged; it does not establish that the earlier review was correct or that rights were properly granted. This implementation distinguishes unreviewed media from changed reviewed media. It performs neither image OCR nor rights assessment.
Check enquiry delivery separately from its visible label
A new label on an enquiry button does not change the actual delivery recipient. The called function, server configuration and service connection may each contain separate settings. I inspect these privately against the new site’s intended connection instead of placing recipient details in a public implementation example. An approved test request can provide separate evidence of delivery when that test is appropriate.
This module does not submit forms or verify email delivery. The public contact method on this website is an email composition link. Applying a form from another project requires checking its visible navigation and its server delivery separately. Correct wording on a screen should not be described as successful receipt of a message.
An implementation that leaves originals untouched
The following reusable checker was implemented for this article. It accepts the public directory, a private marker list and digests of directly reviewed media, then returns findings. It does not delete or replace files. Links and directory junctions that could leave the selected scope are reported instead of followed.
By default, every file in the selected directory is a public candidate. Choosing a different deployment contract is the caller’s responsibility. Unreadable text, text beyond the scan limit and unidentified artifacts do not silently pass. Resolve those findings, run the check again and compare its scope with the actual deployment file list.
Explore the implementation
Python · Read-only public output review
"""Read-only checks for an explicitly selected public artifact directory.
Marker values are private inputs. Results carry marker indexes, never excerpts.
Media approvals are prior human reviews tied to exact bytes, not OCR or rights checks.
"""
import argparse
import hashlib
import html
from html.parser import HTMLParser
import json
from pathlib import Path
import re
import unicodedata
TEXT = {'.html', '.htm', '.css', '.js', '.mjs', '.json', '.xml', '.txt', '.svg', '.webmanifest'}
MEDIA = {'.png', '.jpg', '.jpeg', '.webp', '.gif', '.avif', '.svg', '.mp4', '.webm', '.mov', '.mp3', '.wav', '.ogg', '.pdf', '.zip', '.woff', '.woff2', '.ico'}
SERVER = {'_worker.js', '_headers', '_redirects', '_routes.json'}
def normalized(value):
return ''.join(unicodedata.normalize('NFKC', html.unescape(value)).casefold().split())
class PublicHTML(HTMLParser):
def __init__(self):
super().__init__(convert_charrefs=True)
self.text = []
self.attributes = []
def handle_data(self, value):
self.text.append(value)
def handle_starttag(self, tag, attributes):
self.attributes.extend(value for _, value in attributes if value)
handle_startendtag = handle_starttag
def representations(value, suffix):
result = [value, html.unescape(value)]
if suffix in {'.html', '.htm', '.svg', '.xml'}:
parser = PublicHTML()
parser.feed(value)
result += [''.join(parser.text), *parser.attributes]
# Decode only valid JSON string literals, without evaluating JavaScript.
for match in re.finditer(r'"(?:[^"\\]|\\.)*"', value):
try:
decoded = json.loads(match.group())
if isinstance(decoded, str):
result.append(decoded)
except ValueError:
pass
return [normalized(v) for v in result]
def digest(path):
value = hashlib.sha256()
with path.open('rb') as stream:
for part in iter(lambda: stream.read(1024 * 1024), b''):
value.update(part)
return value.hexdigest()
def scan(root, markers, approved_media=None, contract='all-files', max_text_bytes=16 * 1024 * 1024):
root = Path(root).resolve(strict=True)
if not root.is_dir() or contract not in {'all-files', 'pages-worker-bundle'}:
raise ValueError('Explicit public directory and supported deployment contract required')
markers = [normalized(v) for v in markers]
if not markers or any(len(v) < 3 for v in markers):
raise ValueError('Nonempty private markers of at least three normalized characters required')
approved_media = approved_media or {}
findings, reviewed, excluded = [], 0, []
def flag(relative, reason, **extra):
public_path = relative
if any(marker in normalized(relative) for marker in markers):
public_path = '[identity-path:' + hashlib.sha256(relative.encode('utf-8')).hexdigest()[:16] + ']'
findings.append({'path': public_path, 'reason': reason, **extra})
# Do not follow directory links/junctions. A link itself requires review.
pending = [root]
while pending:
folder = pending.pop()
for path in sorted(folder.iterdir()):
relative = path.relative_to(root).as_posix()
resolved = path.resolve()
if path.is_symlink() or (hasattr(path, 'is_junction') and path.is_junction()) or not resolved.is_relative_to(root):
flag(relative, 'linked-or-outside-root')
continue
if contract == 'pages-worker-bundle' and (relative == '_worker_modules' or relative in SERVER):
excluded.append(relative)
continue
if path.is_dir():
if path.name.casefold() in {'private', '.git', 'node_modules'}:
flag(relative, 'private-directory-in-public-output')
else:
pending.append(path)
continue
reviewed += 1
suffix = path.suffix.lower()
path_text = normalized(relative)
for index, marker in enumerate(markers, 1):
if marker in path_text:
flag(relative, 'known-identity-in-path', marker_index=index)
if path.name.casefold().startswith('.env') or suffix in {'.pem', '.key', '.sqlite', '.db', '.map'}:
flag(relative, 'private-or-unreviewed-artifact')
continue
if suffix in TEXT:
if path.stat().st_size > max_text_bytes:
flag(relative, 'text-too-large-to-check')
else:
try:
views = representations(path.read_text('utf-8-sig'), suffix)
for index, marker in enumerate(markers, 1):
if any(marker in view for view in views):
flag(relative, 'known-identity-in-text', marker_index=index)
except (UnicodeError, OSError):
flag(relative, 'text-unreadable')
if suffix in MEDIA:
expected = approved_media.get(relative)
if not expected:
flag(relative, 'media-needs-human-review')
elif not isinstance(expected, str) or digest(path) != expected:
flag(relative, 'reviewed-media-bytes-changed')
elif suffix not in TEXT:
flag(relative, 'unknown-artifact-needs-review')
return {'passed': not findings, 'reviewed_files': reviewed, 'excluded_by_explicit_contract': excluded,
'findings': findings, 'scope': 'Known markers in selected static output; exact reviewed media hashes. No OCR, remote CMS/R2 enumeration, form delivery or browser rendering.'}
if __name__ == '__main__':
cli = argparse.ArgumentParser(description=__doc__)
cli.add_argument('public_root')
cli.add_argument('--markers', required=True, help='Private JSON array of old identity strings')
cli.add_argument('--media-approvals', help='Private JSON object: public relative path -> reviewed SHA256')
cli.add_argument('--contract', choices=['all-files', 'pages-worker-bundle'], default='all-files')
args = cli.parse_args()
result = scan(args.public_root, json.loads(Path(args.markers).read_text('utf-8-sig')),
json.loads(Path(args.media_approvals).read_text('utf-8-sig')) if args.media_approvals else {}, args.contract)
print(json.dumps(result, ensure_ascii=False, indent=2))
raise SystemExit(0 if result['passed'] else 1)
What the current checks establish
I ran fourteen checks using separate synthetic files: visible wording, entity-encoded alternative text, a name divided by tags, JSON Unicode, filenames, full-width characters, unreviewed media, matching and changed digests, unreadable text, private artifacts, unknown artifacts, an explicit server-bundle contract and the default contract. Input file digests were unchanged before and after each scan.
These results support the code behavior under those conditions. They do not mean every previous website has just been comprehensively rescanned. Remote media content, rendered sharing previews, email delivery and visual review of identifiers inside media were not performed in this check. I keep successful automated checks distinct from direct review that has not yet occurred.
Review earlier public locations as a separate task
A correctly generated new file does not establish that an earlier deployment location has stopped serving old material. This website’s work includes a separate record for retiring its initial preview. Current artifact checks and the continued availability of an earlier public address are different questions. The actual hosting deployment and connection need to be inspected before deciding what to change.
I keep replacement copy, media review, private delivery settings, current deployment artifacts and earlier public locations as separate records. That makes the scope of each check understandable in later work. This module handles known text and media changes in selected public output. It remains useful without claiming to have completed the other reviews.
- Establish the actual public file list.
- Keep known previous identifiers in private marker inputs.
- Review media content and permitted use before recording its digest.
- Inspect delivery settings and test delivery separately when authorized.
- Record both passed checks and unverified scope.