fix(portal): prepare approved resource mappings safely

This commit is contained in:
DeCentN2Madness authored and AIAMarketing committed 2026-11-05 00:40:18 -07:00
1 parent 36d417a422
commit 81492125f4
11 files changed
+427 -153

No files matched your search

+56 -43
View File
@@ -23,71 +23,84 @@ approved release and protected-resource installation commands.
## Source and private collection
Read-only legacy footer source:
`/home/alice/projects/wrk/aia/local-archives/aia-employee-portal-hubspot-footer-source.txt`.
The original stays outside Git as rollback evidence. No access code was available
to the agent. The exact resource count and real content compatibility remain
unverified. Do not infer the earlier approximate 43 links as a measured count.
The 42 approved non-PHI resources have already been collected under the ignored
`private/employee-portal/hubfs/` workspace. Collection and clinical review are
complete. Do not run a new collection pass for production preparation.
From `www`, in the owner's private interactive terminal:
Production preparation is an owner-only, hidden-prompt sequence. The first
command identifies the encrypted streams from the staff and management codes;
the codes are never accepted in arguments, environment variables, files or
output:
```sh
node tools/employee-portal-migrate.mjs collect \
npm run portal:prepare -- identify \
/home/alice/projects/wrk/aia/local-archives/aia-employee-portal-hubspot-footer-source.txt
npm run portal:prepare -- propose-mappings \
/home/alice/projects/wrk/aia/local-archives/aia-employee-portal-hubspot-footer-source.txt
```
Enter each legitimate view code at its hidden prompt. Never place codes in
arguments, environment variables, shell history or reports. The utility checks
both streams, keeps plaintext in memory, and collects only referenced `/hubfs/`
resources from the verified source origin. Other origins require explicit
review/code allowlisting; redirects are bounded and never inherit credentials.
No Jotform, Google or other vendor forms are fetched.
Review the private mapping summary. The role rule maps staff-only references to
`/staff-resources/`, management-only references to `/management-resources/`, and
duplicates a shared source into both protected namespaces. After the owner
approves that operational policy, run:
```sh
npm run portal:prepare -- approve-mappings \
/home/alice/projects/wrk/aia/local-archives/aia-employee-portal-hubspot-footer-source.txt
npm run portal:prepare -- rewrite-production \
/home/alice/projects/wrk/aia/local-archives/aia-employee-portal-hubspot-footer-source.txt
```
The rewrite command prompts for both codes again, rewrites every approved legacy
resource URL, re-encrypts with fresh salts and IVs, verifies each role privately,
and atomically installs `approved-production-ciphertext.json` plus a matching
private `production-readiness.json`. It refuses unresolved `/hubfs/` references.
Prior manifests and production ciphertext are retained as timestamped private
backups.
### Mapping approval versus server verification
`mappingApproved`, `accessPolicyApproved`, and `rewriteApproved` mean the
verified collected source has an approved role and safe protected destination
and may be prepared or installed. They do not claim the destination exists.
`destinationInstalled`, `destinationHashVerified`,
`destinationPermissionsVerified`, and `destinationAccessVerified` remain false
until an authorized VPS rehearsal actually installs the file and verifies its
bytes, `root:nginx` permissions, Basic Auth boundary and protected URL. Local
preparation and installation accept the first state; release acceptance must
still complete the second state before browser cutover.
Read-only legacy footer source:
`/home/alice/projects/wrk/aia/local-archives/aia-employee-portal-hubspot-footer-source.txt`.
The original stays outside Git as rollback evidence. The 42-file collection and
its accepted content are complete; no current command downloads resources.
Private output is `private/employee-portal/`: directories 0700, manifests and
binary resources 0600. This exact subtree is gitignored and outside Astro's
public directory. Filenames are SHA-256 hashes of source URLs, independently
verified by content hashes. The manifest retains original references, labels,
view membership, MIME, bytes, status and delivery/classification decisions.
All new resources default to UNRESOLVED. Publicly reachable does not mean public
approved. Resuming verifies an existing file's content hash; mismatches are not
overwritten. HTML error documents, executable responses, invalid known signatures,
redirects outside approved destinations and resources over 50 MiB are rejected.
The first pass allows only the verified site origin. If a legitimate `/hubfs/`
response redirects to a HubSpot CDN, inspect that exact HTTPS origin first, then
add its origin to the private manifest's `approvedOrigins` list and rerun. Never
approve a redirect based only on a failed download or a broadly shared vendor
domain. Failures remain private review items. The utility prints counts only.
The manifest preserves the original source URL, collection hash, size, reference
membership and accepted source evidence. Public legacy reachability did not
infer public approval.
Before every artifact transfer, verify that only `dist` is copied and that it
contains no private manifest, document or real ciphertext unless that ciphertext
has separately been authorized for the local preview. Never archive the whole
working directory as a deployable artifact.
## Approved rewriting
## Historical collection and rewriting recovery notes
The owner classifies records as PUBLIC-APPROVED, STAFF-INTERNAL,
MANAGEMENT-INTERNAL or UNRESOLVED in the private manifest. Set `deliveryPath`,
`rewriteApproved`, `destinationImplemented`, `destinationVerified`, and (for
internal documents) `destinationAccessControlled` only with actual evidence.
The utility does not create those destinations or infer their approval.
```sh
node tools/employee-portal-migrate.mjs rewrite \
/home/alice/projects/wrk/aia/local-archives/aia-employee-portal-hubspot-footer-source.txt
```
It reauthenticates both views, changes approved quoted href/src attributes only,
preserves unrelated HTML/labels, and re-encrypts with fresh salts and IVs. It
verifies round-trip decryption and distinct codes before writing new ciphertext
with exclusive filenames. The original source is untouched. CSS/srcset mappings
require separate review and are rejected rather than silently rewritten.
Unresolved links retain their original destination as a recorded dependency,
not a claim of verified destination health. A changed manifest alone never
changes links in the encrypted stream.
The original source, collection manifests, collected bytes and prior ciphertext
revisions remain recovery evidence. Do not change their classifications or
destination flags by hand; use the production-preparation sequence above so
stream identity, references, source hashes and protected destinations remain
consistent. CSS/srcset dependencies remain an explicit error rather than a
silent rewrite.
## Local preview and content safety
Use a private ciphertext file generated by `collect` or `rewrite`:
Use a retained private ciphertext revision:
```sh
AIA_PORTAL_LOCAL_PREVIEW_APPROVED=true \
+9 -4
View File
@@ -61,7 +61,6 @@ npm run deploy:rollback -- FULL_APPROVED_SHA --confirm
## Employee resources: separate one-time operation
Approved non-PHI employee files are never part of an ordinary release. After
the website release that contains `portal-resource-install.sh` is promoted and
the VPS SOPS-backed Basic Auth material is independently ready:
```sh
@@ -72,15 +71,21 @@ AIA_DEPLOY_SSH_IDENTITY="$HOME/.ssh/AIAMarketing" \
npm run deploy:portal-resources -- --confirm
```
The installer invokes the promoted root-only `portal-resource-install.sh` through
NixOS's base `/bin/sh` and coreutils path; it does not depend on a globally
installed Node.js runtime. It reads only the private collection manifest,
For the first install, the client extracts `portal-resource-install.sh` from the
current reviewed Git commit, uploads it into the root-controlled commit staging
directory, verifies its SHA-256 remotely, and invokes it through NixOS's base
`/bin/sh` and coreutils path. A prior public site release and a globally installed
Node.js runtime are not prerequisites. It reads only the private collection manifest,
accepts approved `STAFF-INTERNAL` and `MANAGEMENT-INTERNAL` mappings to
`/staff-resources/` and `/management-resources/`, verifies every local hash, rejects links/traversal and
uploads a root-controlled temporary tree. It never uses `--delete` for resource
directories and refuses to replace a different existing file. The VPS installs
regular files as `root:nginx` mode `0640` and reports metadata only.
Local mapping approval authorizes preparation and installation only. After the
authorized VPS rehearsal, verify installed hashes, ownership/mode, staff versus
management Basic Auth, and both protected URLs before deploying portal links.
Before public DNS cutover, test the site with approved host-header/SNI methods,
including the portal’s noindex response, valid ciphertext unlock, both protected
resource namespaces, denial cases, redirects, Form A, IPv4 and IPv6. Portal