Files
claudetools/session-logs/2026-06/2026-06-21-howard-security-assessment-scoring.md
Howard Enos e2ad87417e sync: auto-sync from HOWARD-HOME at 2026-06-21 17:45:23
Author: Howard Enos
Machine: HOWARD-HOME
Timestamp: 2026-06-21 17:45:23
2026-06-21 17:45:49 -07:00

123 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Session — security.azcomputerguru.com: posture scoring + gap/upsell findings engine
## User
- **User:** Howard Enos (howard)
- **Machine:** Howard-Home
- **Role:** tech
## Session Summary
Built out the ACG Security Assessment tool (`security.azcomputerguru.com`, the `security-assessment`
submodule) per Howard's brief: make it user-friendly, "better calculate," add more ACG-fit questions,
and surface upsell gaps to both onboard a customer and find service gaps. The tool was a clean
single-assessor PHP+MySQL wizard (Syncro lookup → risk-ordered questionnaire → 365/Google consent →
flat export) with NO scoring and NO gap→service mapping. Confirmed two design choices with the user up
front: scoring model = 0100 risk score per domain + overall AF grade; output = two views (internal
upsell worklist + client-safe risk report).
Designed a data-driven scoring engine: scoring rules live in `questions.json` (per-answer `risk`
weights + a `finding` with why/fix + the ACG `service` that closes the gap), and the engine is mirrored
in JS (live wizard) and PHP (export), both reading the same rules. Rewrote `questions.json` to v2 —
added `risk`/`finding` metadata to 25 fields, a `scoring` block (AF grade thresholds, severity→weight
critical15/high10/medium6/low3, gapThreshold, `requiredControls`), and 6 new ACG-relevant questions
(3rd-party 365/Google backup, dark-web monitoring, DNS filtering, MDR/SOC, IT documentation, vCIO
cadence). Validated the engine in node against weak (0/F, 25 findings) and strong (100/A, 0) sample
clients.
Implemented the engine + UI in `index.php`: live posture chip (overall grade) in the topbar, per-domain
grade badges in the rail, and a rich "Posture & Findings" results view (overall AF card, per-domain
score bars, prioritized findings each mapped to the ACG service, and an internal↔client-safe toggle,
with a collapsible full intake). Mirrored the engine in `api.php` (`score_assessment`) and rewrote the
export to render posture + findings, honoring `?view=internal` (default; shows ACG service per gap +
raw intake) vs `?view=client` (client-safe risk/recommendation report, no upsell language).
Then drove a 6-item todo list to completion of the code-doable work: responsive/mobile layout
(breakpoints; rail → horizontal stepper); replaced all `alert()` dialogs with an in-page saved-list
modal + toasts + light input validation; compliance-aware framing (when the client reports compliance
drivers or sensitive data, findings in `requiredControls` get a REQUIRED badge + a context banner).
Committed the build on `feature/posture-scoring-and-findings`, pushed, then (per Howard) merged
fast-forward to `security-assessment` `main` (`c82a3c9`) and deleted the branch. Two items remain:
GuruRMM endpoint prefill (deferred — infra) and the live IX deploy (gated on go + access).
## Key Decisions
- **Scoring rules as DATA in questions.json, engine duplicated in JS + PHP.** Keeps the wizard's live
score and the export identical without a shared runtime; the only duplication is ~30 lines of engine
in each language.
- **Risk score + AF grade** (not CIS/NIST maturity or pure insurance checklist) — user choice;
simplest + most sales-legible.
- **Two export views** (`?view=internal` default vs `?view=client`) — internal carries the ACG service
per gap; client view strips upsell language and frames gaps as risks/recommendations.
- **Domain score only counts ANSWERED scorable fields** (unanswered = not assessed, not penalized) so a
mid-consult partial intake still shows a meaningful posture; "Unsure" answers map to a mid risk frac.
- **Compliance framing via a `requiredControls` field-id list** (not per-framework rules) — when the
client has compliance drivers/sensitive data, those baseline-control gaps get a REQUIRED badge. Robust
+ low-maintenance vs brittle per-framework mapping.
- **GuruRMM prefill deferred, not faked** — no Syncro→RMM client mapping exists in the RMM schema and no
reachable read API from the public IX host; a fuzzy name-match could prefill the wrong client's counts
into a client-facing report. Count fields stay manually editable, so nothing is broken.
- **Merged to submodule main, no auto-deploy** — DEPLOY.md is a manual cPanel upload, so merging to main
does not change the live site; the live deploy stays a gated, confirmed step.
## Problems Encountered
- **Export Edit failed to match** — the live rows used unquoted `class=r`/`class=k`; re-read the exact
block and matched it.
- **node `require('./questions.json')` failed** from a /tmp test script (require is script-relative, not
cwd) — used an absolute path.
- **No PHP locally** — validated JSON + the wizard JS in node (engine is identical logic) and the export
PHP via careful review + a bracket-balance check; final PHP gate is the live host.
## Configuration Changes
In the `security-assessment` submodule (committed `c82a3c9`, now on `main`, pushed):
- `app/questions.json` — v2: scoring rules + `requiredControls` + 6 new questions.
- `app/index.php` — scoring engine (JS), posture chip + rail badges, Posture & Findings view, responsive
CSS, toast/modal, validation, view toggle.
- `app/api.php` — PHP scoring engine (`score_assessment`/`grade_for`) + rewritten export (posture +
findings + `?view` internal/client + compliance banner).
- `README.md`, `DEPLOY.md`, `app/config.sample.php` (the last two carried pre-existing Jun 19 changes:
howard@ added to the allow-list; DEPLOY tweaks).
No code committed to the ClaudeTools repo from this work (only this session log + the submodule pointer,
which `/sync` already tracks at `c82a3c9`).
## Credentials & Secrets
None created or discovered. The tool's config uses existing vault entries (referenced in
`config.sample.php`): DB `msp-tools/security-assessment-db`, Syncro `msp-tools/syncro-mike`; M365
Security Investigator app `bfbc12a4-f0dd-4e12-b06d-997e7271e10c` (read-only, multi-tenant). `config.php`
is gitignored / lives on the server.
## Infrastructure & Servers
- security.azcomputerguru.com — PHP + MySQL on the IX cPanel host (172.16.3.10), behind Cloudflare
Access (Zero Trust app `8ce5f31c-4f4e-4883-bae1-f7606e5b06c0`; allow mike@ + howard@). DB `acgsec_assess` / user `acgsec_app`.
- Consent redirect: `https://security.azcomputerguru.com/consent-callback.php`.
- security-assessment repo: Gitea `azcomputerguru/security-assessment`; `main` now `c82a3c9`.
## Commands & Outputs
```
# engine validation (node)
node engine-test.js # WEAK -> 0/100 F, 25 findings ; STRONG -> 100/100 A, 0 findings
# parse/lint checks
node -e "require('.../questions.json')" # JSON OK (25 scorable, requiredControls 13)
new Function(<index.php <script>>) # wizard JS parses OK (200 lines)
# land
git checkout -b feature/posture-scoring-and-findings; git commit; git push -u origin <branch>
git checkout main; git merge --ff-only <branch>; git push origin main # c3ca261..c82a3c9
git branch -d <branch>; git push origin --delete <branch>
```
## Pending / Incomplete Tasks
- **#1 GuruRMM endpoint prefill** — DEFERRED (infra): no Syncro→RMM mapping + no reachable RMM API from
IX. Unblock: add a Syncro id to GuruRMM clients (or name-match + confirm) and expose a read-only RMM
API reachable from IX with a key, then a config-gated lookup with graceful fallback.
- **#6 Deploy to IX + live smoke test** — GATED on go + access: manual upload of `app/index.php`,
`app/api.php`, `app/questions.json` to the cPanel docroot (config.php already on server); then smoke
test lookup, live posture/findings, and both export views behind Cloudflare Access. HOWARD-HOME may
lack SFTP/SSH to the IX docroot (IX SSH key is from GURU-5070).
- Optional: number-field scoring (global_admins), client-report exec-summary/branding polish.
## Reference Information
- Submodule commit: `c82a3c9` on `security-assessment` main (claudetools pins it as of sync `68a05d3`).
- Export views: `api.php?action=export&id=<id>&view=internal|client`.
- Scoring: domain = 100·(1 Σ(weight·riskFrac)/Σweight) over answered scorable fields; overall = domains
weighted by points; grades A≥90 B≥80 C≥70 D≥60 else F; finding when riskFrac ≥ gapThreshold (0.5).
- Companion logs this session: `2026-06-21-howard-unifi-pfsense-control-verbs.md`,
`2026-06-21-howard-gururmm-bug-018-019.md`.