Workflow-App/Workflow/scripts/_check_signature_images.py
sjcoetzeesj-coder 11d0178ab1 feat: commit Workflow app source; move CIPC credentials to env
Adds the full Next.js app (app/, components/, lib/, scripts/, assets/,
docs/, Dockerfile, compose, configs) to version control.

Security:
- CIPC BizPortal customer-code logins (Login 1 + Login 3) now read
  CIPC_LOGIN1_CODE / CIPC_LOGIN1_PASSWORD / CIPC_LOGIN3_CODE /
  CIPC_LOGIN3_PASSWORD from .env.local instead of source.
- BizPortal personal login in apply-bizportal route reads
  BIZPORTAL_ID_NO / BIZPORTAL_PASSWORD.
- Added .env.example as the committed template.

.gitignore excludes .env*, node_modules, .next, logs, sqlite, Playwright
storage_state.json (live CIPC cookies), scratch dirs, client PDFs and
debug screenshots.
2026-10-18 15:21:45 +02:00

86 lines
No EOL
3.1 KiB
Python

"""Decisive test: does a file contain embedded signature IMAGES?
Text extraction can't tell a signed scan from a blank template — both have the
same text layer. Embedded image XObjects can: a scanned signature leaves an
image where the template has bare underscores.
"""
import io
import urllib.parse
import urllib.request
from pathlib import Path
from pypdf import PdfReader
JEI = Path(r"C:\Users\SJ\.minimax-agent\projects\JEI")
env = {}
for line in (JEI / ".env.local").read_text(encoding="utf-8").splitlines():
line = line.strip()
if line and not line.startswith("#") and "=" in line:
k, v = line.split("=", 1)
env[k.strip()] = v.strip().strip('"').strip("'")
PB = env.get("NEXT_PUBLIC_PB_URL") or env["PB_URL"]
TOK = env["PB_TOKEN"]
FILES = [
("LOCAL UFC - Shareholders Resolution ... (attached)",
None,
r"C:\Users\SJ\.minimax\v2\assets\2026\10\12\13-41-56-012-asset_20261012-134156-012_a0299b3ae338_88b7c484-UFC - Shareholders Resolution Director Sune Resigned.pdf"),
("LOCAL UFC - Sune Resignation Letter (attached)",
None,
r"C:\Users\SJ\.minimax\v2\assets\2026\10\12\13-41-56-017-asset_20261012-134156-017_9aec1b4380ba_261a3e6b-UFC - Sune Resignation Letter.pdf"),
("STORED resolution.signed_letter", ("pbc_266170881", "e0dbxprazb70rqx", "corrected_v2_avwpnq7o93.pdf"), None),
("STORED resignation.letter", ("ysterbaard_director_resignations", "8cfie18uhgy0n0o",
"resignation_letter_the_ultimate_fuel_company_1jk8t34nf3.pdf"), None),
("STORED resignation.signature", ("ysterbaard_director_resignations", "8cfie18uhgy0n0o",
"tbde1evt2m6_8mz8kutpki.pdf"), None),
]
def count_images(reader) -> int:
n = 0
for page in reader.pages:
try:
n += len(page.images)
except Exception:
pass
return n
def has_annot_sig(reader) -> int:
n = 0
for page in reader.pages:
try:
for a in page.get("/Annots") or []:
obj = a.get_object()
if obj.get("/Subtype") == "/Widget" or obj.get("/FT") == "/Sig":
n += 1
except Exception:
pass
return n
for label, pb, path in FILES:
if pb:
col, rid, fname = pb
url = f"{PB}/api/files/{col}/{rid}/{urllib.parse.quote(fname)}"
req = urllib.request.Request(url, headers={"Authorization": TOK})
with urllib.request.urlopen(req, timeout=120) as r:
b = r.read()
else:
b = Path(path).read_bytes()
rd = PdfReader(io.BytesIO(b))
imgs = count_images(rd)
sigs = has_annot_sig(rd)
# Look for signature-ish words in the text layer
txt = " ".join((p.extract_text() or "") for p in rd.pages).lower()
markers = []
for m in ("approved by", "signed at", "signature", "signed"):
if m in txt:
markers.append(m)
print(f"{label}")
print(f" size={len(b):>8} B pages={len(rd.pages)} images={imgs} digital-sig-widgets={sigs}")
print(f" text markers present: {markers}")
print()
print("VERDICT: images==0 on every file => none of these contain a scanned")
print("signature; all are blank templates with signature lines only.")