Open Questions

Sixteen questions the assessment could not settle from source. Most are a one-minute internal lookup, and several decide a severity.

The assessment ran against source code and configuration. Sixteen questions could not be settled that way, and it recorded each one with what would answer it and what answering it costs.

This is the highest-value page on the site for the smallest amount of work. Several of these are a single read-only lookup in a console you already have access to, and each one either removes a finding, bounds a severity, or tells you a whole epic is unnecessary.

Do these first

Five questions, and each one changes what you file.

Question Answers Cost to you
Q-1 Whether a leaked token is High or Critical, and whether you owe incident response over 14 services One read-only lookup, one minute
Q-4 Whether you have any security telemetry at all. Bounds a whole root cause One console screen or a read-only API call
Q-5 Whether the Hasura superuser mapping is High or Critical One Cognito console screen
Q-11 Whether the developer boxes really publish an IDE with no password. Decides whether one High chain is live A public image pull, no access to your systems
Q-12 Whether branch protection exists, and whether your plan even offers it One screen

The register

Q-1: Does the leaked Cloudflare Access service token authorise the fourteen internal services recorded against its client id?

Status: Open. Bounds MGR-H24 and W-7: High or Critical.

The assessment calls this the highest-value unresolved question in the engagement. It decides two things: whether that weakness is High or Critical, and whether an incident-response obligation exists over fourteen services’ access logs, including the operations console, the production database, the mail service and webmail.

The existing High rating is earned rather than assumed. It rests on a certificate parse covering all 18 trust-list certificates, which bounds the certificate half of the finding and says nothing about the Access half. That is the half this question opens.

How to settle it: in the Cloudflare Zero Trust dashboard, look up the service token by its client id and list the Access applications whose policies include it. One read-only lookup, one minute.

Do this before you size the credential epic. It is the difference between a rotation ticket and an incident.

Q-2: What do the two Cloudflare Access applications actually enforce?

Status: Open. Bounds W-1 severity, W-6 magnitude, and whether a proposed load balancer change is safe.

Three Access policies exist in the infrastructure code as UUID references with every decision field null. The code records that a policy is attached and records nothing about what it decides.

This matters three times over. It bounds how bad the unauthenticated tool server is, it bounds the public upload portal’s exposure, and it decides whether the default-action change proposed in MGR-H18 will strand something.

How to settle it: the Cloudflare Zero Trust console.

Q-3: Is anything deployed from the stale forks, in particular the homeserver at 306 commits behind?

Status: Open.

The chat client is Matrix-backed, so this is a live possibility rather than a hypothetical.

The answer from source is that it is unanswerable from source, and that is itself the finding. No workflow builds the homeserver image. No Dockerfile for it exists in any of the 32 clones. The design documents and the shipped infrastructure code disagree about its base image. The production infrastructure digest-pins the image, which is good practice over an input nothing in the estate can build.

How to settle it: read the registry manifest, or find the missing Dockerfile.

File this one as an epic regardless of the answer. A production image that no repository can rebuild is a supply-chain gap whether or not this particular fork is behind.

Q-4: Do CloudTrail, GuardDuty, Security Hub, AWS Config or VPC flow logs exist outside Terraform?

Status: Open. Bounds RC-8 entirely, and W-9.

The assessment calls this the largest single unknown in the review. Absence from 93 Terraform files proves the absence of a codified control, not the absence of a control. Someone may have clicked these on years ago.

It also bounds the dwell time on every credential finding. Without audit logs you cannot answer “was this used”, which is the question every one of the committed-credential findings raises.

How to settle it: the AWS console, or a read-only API call.

Do this before you write RC-8’s epic. If the answer is that they all exist, the epic collapses into “codify what is already on”. If the answer is that none exist, RC-8 is real and structural.

Q-5: How many people hold the three Cognito groups?

Status: Open. Bounds W-2: High or Critical.

The number of people currently holding database superuser through the group mapping in MGR-H20 is in no file. It is the difference between High and Critical.

How to settle it: one Cognito console screen.

Q-6: Does the development load balancer behind chat-staging, chat-testing and hasura-staging serve production rows?

Status: Partially answered, 2026-08-22. The Access gate does not bound the load balancer, and staging holds production-derived rows. See below.

One load balancer is reached by three names: one proxied and Access-gated, two not. The staging identity pool was repopulated with the real users. Whether the target groups behind it read production data is unsettled.

Why this is worth answering early. A weakness in staging is normally bounded by staging holding less interesting data. The personal data inventory records production-to-staging replication every five minutes, which removes that bound. This question asks the direct version.

How to settle it: the load balancer’s target-group configuration and the database instance its tasks connect to.

Partial answer, 2026-08-22, from work on an unrelated ticket. Two of the three limbs are now settled, and the answer is worse than the question assumed.

The Access gate on the proxied name does not bound the load balancer. phenom-dev-alb is internet-facing with 0.0.0.0/0 ingress on ports 80 and 443, and it serves the same Hasura directly. Through the gated hostname a health probe returns 302 to the Cloudflare Access login. Straight at the ELB hostname it returns 200 and a live engine version. So the third name being proxied and Access-gated buys nothing that the other two do not already give away: anyone who knows the ELB hostname skips Access entirely. Filed as phenom-infra#274.

The data question is effectively answered, from configuration rather than from probing. phenom-infra/environments/development/locals.tf:13 records that the development database was cloned from a production snapshot on 2026-06-20 and still carries production’s master username. Staging holds production-shaped rows by construction, independent of the five-minute replication the personal data inventory describes. Treat a staging weakness as a production-data weakness.

What bounds this today, and how far. HASURA_GRAPHQL_ENABLE_CONSOLE is false, so there is no console. Unauthenticated callers land on role public, and public was confirmed unable to read users.email. What else public can read has not been enumerated, and that measurement is the first task on #274. HASURA_GRAPHQL_ENABLED_APIS includes metadata and pgdump, so the admin secret is the only thing between the open internet and a schema dump.

Still open: whether the target groups behind the two grey-cloud chat names read the same rows, and the enumeration of role public.

Q-7: Of the 17 hostnames that returned no answer, which are decommissioned, which were never deployed, and which are internal-only?

Status: Open.

Two are classified as referenced in production routing and not resolving, two as probably scratch, and thirteen are undetermined.

How to settle it: the Cloudflare zone export plus the AWS account.

This is a housekeeping question with a security edge: a name in production routing that does not resolve is a name someone else can make resolve.

Q-8: Is thephenom.ai client-controlled, and what resolves under it?

Status: Open.

A production load balancer carries a .ai hostname as a host-header match value on four listener rules, and your own runbook calls the name unused and pending cleanup. The DNS capture covered .app only, so this domain was never measured.

How to settle it: a passive resolution of that name plus a registrar lookup.

Answer this one soon. Four production listener rules matching a hostname whose ownership is unconfirmed is a small question with an unpleasant worst case.

Q-9: Is ghostmode deployed from the artifact its Dockerfile builds?

Status: Open. Bounds every ghostmode figure in the assessment.

The deployment file declares itself a capture of a third party’s deployment and carries no build key. Several ghostmode severities turn on this, including MGR-H04 and MGR-H05.

How to settle it: the running host, or the registry digest.

Cost note that does not apply to you: the assessment priced this as organizational, because the host belongs to a third party. If that host is yours, this is a one-command answer. If it genuinely belongs to a third party, that is worth knowing on its own.

Q-10: Do the committed object-store files carry credential values?

Status: CLOSED, NEGATIVE. Verified.

Recording this one because a clean negative is worth as much as a finding, and because of how it was settled.

The seven committed objects were extracted, the container parsed, and the payload decoded. The configuration object is a complete, well-formed, plaintext default configuration: 37 sub-systems, 242 key/value pairs of which 117 are non-empty. Every non-empty value is a default tunable. All 31 credential-bearing keys carry the empty string, including the directory bind password, the identity client secret and every token, password and key field. There is no user or service-account path in the tree. No value was transcribed.

What survives, reframed. Committing object-store server state to git is still a hygiene defect: the format marker publishes the deployment id and the erasure-set uuid. The correct characterisation is committed server state and deployment identifiers, no credential values. File it as hygiene, not as credential exposure.

Q-11: Does the code-server base image fall back to no authentication when neither password variable is set?

Status: Open. Decides whether one High chain is live.

This is the direct question behind MGR-H25. The provisioned developer boxes never set the password variable, so the posture is a property of the upstream image’s default.

How to settle it: a public image pull inside a container. Minutes, and it needs no access to any of your systems.

This is the cheapest High-bounding question in the register. Nothing stops you doing it right now.

Q-12: Is branch protection configured, and what is the GitHub plan tier?

Status: Open. Bounds RC-7 entirely.

Three of your own documents state at the snapshot that the capability is unavailable on the current plan. If that is right, RC-7 is not a configuration failure, it is a purchasing decision that nobody has been asked to make, and the epic changes shape completely.

How to settle it: one screen.

Q-13: Two coverage numerators have not been recounted since the work that changed them

Status: Open, and it is a defect in the assessment’s own reporting rather than in your systems.

Test-procedure coverage was stated as 65 of 76 before a later round of work read three blocking files and re-derived it to 68 of 76. The denominator of 76 is verified and reproducible. An older report file still states 65, still cites 174 findings against 184 on disk, and remains internally inconsistent.

What this means for you. If someone circulates a summary quoting 65 of 76 or 174 findings, it is the stale file. This documentation set uses 68 of 76 and 184, and treats 184 itself as provisional for the reasons on the findings register.

Q-14: Buzzard-iOS is covered by no finding at all

Status: Open. This is a coverage gap, not a clean bill of health.

The iOS client was excluded from the review on size and never generally examined. Its Firestore rules carry allow read, write: if true on two recursive wildcards, verified verbatim during the data-mapping work. Firestore rules are live server-side authorization policy, not a client-side setting. A second repository is in the same position on coverage.

How to settle it: a targeted review. Offline, no approval needed.

Q-15: DMARC and DKIM posture

Status: Open. Bounds the email limb of W-5.

The DNS capture queried 49 names for seven record types and never queried the DMARC record or any DKIM selector, because the script iterates a host list and constructs no underscore-prefixed names. An apex TXT query cannot return a DMARC record.

The posture is unknown, not absent, and the assessment corrects itself here: an earlier draft said absent and stamped it Verified.

This bounds the surface every account-recovery mail in the estate traverses, which is the same surface as the password-reset page weakness. SPF is measured and soft-fails.

How to settle it: two passive TXT queries against a public resolver. Minutes.

Q-16: Does the Hasura engine’s reserved admin role consult permission blocks?

Status: Open. Bounds W-2 blast radius and the column breadth of the data-exposure findings.

Not sourceable from anything the review held: the local corpus contains zero Hasura files, verified by search. Meanwhile the estate asserts in five of its own artifacts that admin is a built-in superuser needing no permission block, asserts in three of those five that defining an admin block is “rejected as inconsistent”, and ships 13 such blocks on the homeserver source.

How to settle it: published documentation for the pinned engine version, or the engine source. Offline.

There is a cheaper second question riding on the first. If the engine does report those 13 blocks as inconsistent, the production deploy workflow swallows the verdict with a trailing || true, so nobody would ever see it. Reading one production deploy log answers it, and the || true is worth a ticket on its own regardless of what the log says.


Filing these

Do not file sixteen tickets. File one triage ticket carrying the four questions in the table at the top, assign it to whoever holds console access, and time-box it to an afternoon. The answers change the shape of at least three epics before anyone writes code.

Then file the two that are real work regardless of the answer: Q-3 (a production image nothing can rebuild) and Q-14 (an unreviewed client with an unconditional Firestore allow).