@jimhoyd/urlcode-admin 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/IMPLEMENTATION-STATUS.md +28 -0
- package/LICENSE +202 -0
- package/README.md +292 -0
- package/SECURITY.md +19 -0
- package/THREAT-MODEL.md +34 -0
- package/UX-REVIEW.md +61 -0
- package/dist/admin-account.d.ts +15 -0
- package/dist/admin-account.js +82 -0
- package/dist/admin-audit-export.d.ts +7 -0
- package/dist/admin-audit-export.js +38 -0
- package/dist/admin-copy.d.ts +64 -0
- package/dist/admin-copy.js +71 -0
- package/dist/admin-dashboard.d.ts +2 -0
- package/dist/admin-dashboard.js +14 -0
- package/dist/admin-detail.d.ts +14 -0
- package/dist/admin-detail.js +27 -0
- package/dist/admin-health.d.ts +24 -0
- package/dist/admin-health.js +33 -0
- package/dist/admin-presentation.d.ts +3 -0
- package/dist/admin-presentation.js +20 -0
- package/dist/admin-recovery.d.ts +24 -0
- package/dist/admin-recovery.js +78 -0
- package/dist/admin-reporting.d.ts +14 -0
- package/dist/admin-reporting.js +161 -0
- package/dist/admin-runtime.d.ts +16 -0
- package/dist/admin-runtime.js +22 -0
- package/dist/admin-screens.d.ts +32 -0
- package/dist/admin-screens.js +61 -0
- package/dist/admin-templates.d.ts +13 -0
- package/dist/admin-templates.js +76 -0
- package/dist/admin-ui.d.ts +65 -0
- package/dist/admin-ui.js +71 -0
- package/dist/admin-user-export.d.ts +3 -0
- package/dist/admin-user-export.js +57 -0
- package/dist/admin-users.d.ts +13 -0
- package/dist/admin-users.js +19 -0
- package/dist/admin.d.ts +37 -0
- package/dist/admin.js +304 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +16 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +6 -0
- package/dist/scaffold.d.ts +38 -0
- package/dist/scaffold.js +46 -0
- package/dist/support-banner.d.ts +11 -0
- package/dist/support-banner.js +43 -0
- package/package.json +56 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Admin implementation status
|
|
2
|
+
|
|
3
|
+
Status: `0.1.0-alpha.1`, the first npm alpha, published through the tag-driven release workflow after core `0.4.0-alpha.1`, ui `0.1.0-alpha.1` and auth `0.1.0-alpha.1` (core issue #78). The implemented admin workflows and reviewed shared-presentation work are merged to main. Production release validation remains separate. Cross-repository acceptance: https://github.com/jimhoyd-com/urlcode/issues/58. Core extension integration PR #59 is merged. Auth owns identities, sessions and transactional authority checks; this package owns the trusted administration interface.
|
|
4
|
+
|
|
5
|
+
Implemented: scoped internal authorization; no impersonated admin access; fresh, reasoned mutations; account search, compound filters, sorting, stable live pagination, details, setup invitation and bounded export; audited full-email reveal; lock/unlock/role assignment; global and individual session revocation; role definitions; audit pagination; registration approval; dual-approval security cases with notes/closure; opt-in short-lived impersonation with required notice; shared safe presentation; auth/admin initialization with private operator storage.
|
|
6
|
+
|
|
7
|
+
## Additional implemented acceptance
|
|
8
|
+
|
|
9
|
+
- Complete filtered-user exports (5,000 records/4 MiB/5 seconds, all-or-nothing), full-range audit exports, staged bulk role/verification and individual verification/password-reset/email-change/deletion/method-removal workflows.
|
|
10
|
+
- Structured account detail sections for overview, methods, sessions, recovery, activity and consent/data; escaped administrator notes are audited. Method operations are a linked fresh-authentication view.
|
|
11
|
+
- Manual recovery evidence, two distinct authorized reviewers, private delivery to replacement address and warning to old address, browser redemption and mandatory factor reenrollment. This is not automated identity verification; recovery contacts/public intake remain later auth scope.
|
|
12
|
+
- Shared localized email copy, typed health adapter, host support-banner wrapper, and synthetic browser walkthrough of dashboard/search/detail.
|
|
13
|
+
- Embedded hosts can use `createAdministrationRuntime` to install the support banner and live runtime health together; an actual-runtime regression verifies cached application pages, impersonation, revocation and admin denial. Every request must use the returned runtime. The stock CLI still requires host integration; arbitrary frontend scripts remain outside the trusted UI guarantee.
|
|
14
|
+
- Session account/device/created-time filters, persisted passkey/provider added/last-used dates, and dashboard linked totals/SVG activity charts. Historic method timestamps remain unknown rather than being invented.
|
|
15
|
+
- Kit adoption (urlcode-admin issue #10, core plan §7.2) is implemented: every console screen is an `admin/*` kit template with a declared view model and sample view (`adminTemplates`, `adminUiTemplates`); `adminExtension({ ui })` renders through `ui.kit` when the host supplies the `ui` extension and through the shared primitives inside the console shell otherwise. The HTTP suites run under both render paths; a doctor-style suite renders every template with its sample and with the view a real request computes, checks escaping of user-controlled values on kit pages, the strict CSP, `no-store` and the immutable hashed stylesheet. The compact UI pass supplies the console shell, sidebar icons, metrics, tables, forms and theme toggle through the pinned shared UI stylesheet. Both kit and primitive paths have clean-install checks and a local light/dark browser walkthrough; broader accessibility and device assessment remains separate.
|
|
16
|
+
|
|
17
|
+
## Remaining first-release acceptance
|
|
18
|
+
|
|
19
|
+
- Connect live operator runtime/provider/sender observations to the health adapter. Activity data begins when the feature is activated.
|
|
20
|
+
- Full accessibility assessment and broader browser/device/deployment validation. The current walkthrough is not WCAG conformance evidence.
|
|
21
|
+
- Refresh package and CI evidence whenever code or dependency pins change. The private auth/UI credentials are configured, core #64/#69 are closed, and hosted verification passes on Node 22/24/26. See ACCEPTANCE.md for the exact baseline.
|
|
22
|
+
|
|
23
|
+
Role definitions remain reviewed operator configuration. User role assignments are administrative transactions. This preserves the separation between changing authority definitions and assigning already-reviewed authority.
|
|
24
|
+
|
|
25
|
+
## Recorded acceptance
|
|
26
|
+
|
|
27
|
+
See [ACCEPTANCE.md](ACCEPTANCE.md) for exact merged revisions, automated coverage,
|
|
28
|
+
clean-install evidence and the remaining operational validation boundary.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright 2026 Jimmy Ho
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# URLCode admin
|
|
2
|
+
|
|
3
|
+
An optional administration extension backed by URLCode auth's service API. It supplies permission-gated pages for accounts, roles, sessions, audit records, registration approvals, administrative cases and explicit support impersonation. It does not replace URLCode's private runtime management endpoints or edit project YAML.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/jimhoyd-com/urlcode-admin/actions/workflows/verify.yml)
|
|
6
|
+
|
|
7
|
+
This is an actively reviewed Node/SQLite implementation. Still outstanding: live operator runtime/provider/sender observations feeding the health adapter, a full accessibility and broader browser/device/deployment assessment, and refreshed package/CI evidence whenever code or dependency pins change. Local tests are not independent security review, real-provider deployment evidence or an accessibility certification. See [SECURITY.md](SECURITY.md).
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install @jimhoyd/urlcode @jimhoyd/urlcode-ui @jimhoyd/urlcode-auth @jimhoyd/urlcode-admin
|
|
13
|
+
npx urlcode init my-site --with auth,admin
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`@jimhoyd/urlcode-admin` is published to npm as an alpha (`0.1.0-alpha.1`). Alpha releases can change exported names, the console's routes and the scaffold output between versions without a deprecation period; pin exact versions in an operator directory and read the release notes before upgrading. The package declares its peers by version range (`@jimhoyd/urlcode >=0.4.0-alpha.1 <0.5.0`, `@jimhoyd/urlcode-ui` and `@jimhoyd/urlcode-auth >=0.1.0-alpha.1 <0.2.0`), so install all four together; npm resolves them from the registry. Every release is built by the tag-driven [release workflow](.github/workflows/release.yml), signed with a GitHub attestation and published through npm trusted publishing, so `gh attestation verify jimhoyd-urlcode-admin-<version>.tgz --repo jimhoyd-com/urlcode-admin` and `npm audit signatures` can check what you downloaded. Publishing is still not a security review, real-provider deployment evidence or an accessibility certification.
|
|
17
|
+
|
|
18
|
+
## Build from reviewed local repositories
|
|
19
|
+
|
|
20
|
+
The published packages are the supported path; building from source remains available for deployments that must review and pin exact commits rather than registry versions. The core runtime must include the reviewed generic extension contract from [core PR #59](https://github.com/jimhoyd-com/urlcode/pull/59) or an approved successor; the version number `0.3.0` alone is insufficient. Use an exact reviewed core commit and clean committed source trees.
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
node scripts/pack-sources.mjs \
|
|
24
|
+
--core /absolute/source/urlcode \
|
|
25
|
+
--auth /absolute/source/urlcode-auth \
|
|
26
|
+
--ui /absolute/source/urlcode-ui \
|
|
27
|
+
--admin /absolute/source/urlcode-admin \
|
|
28
|
+
--out /absolute/new-private-package-directory
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`--core-revision` defaults to the `urlcode` entry of [`peers.json`](peers.json), the single record of the exact core/auth/UI revisions verified with this source checkout (source CI and [ACCEPTANCE.md](ACCEPTANCE.md) read the same file; published releases resolve peers from the registry by version range instead); pass it explicitly to pack against another reviewed commit. The helper runs lockfile installation without lifecycle scripts, installs the peers from locally built tarballs instead of the registry, typechecks/builds, packs and records commit/integrity metadata. Nothing is published. `--offline` requires an existing dependency cache; `--skip-install` reuses third-party dependencies. Neither bypasses the reviewed revision/clean-tree requirement. Run each repository's full `npm run verify` separately.
|
|
32
|
+
|
|
33
|
+
Install the resulting core, UI, auth and admin tarballs together in your operator directory, using filenames recorded in `source-manifest.json`. Follow auth's scaffold/bootstrap procedure first, or run `urlcode-admin init --directory NEW_DIRECTORY`, which wires both auth and admin into the generated host and route project; review the result before activation.
|
|
34
|
+
|
|
35
|
+
## Wiring
|
|
36
|
+
|
|
37
|
+
Declare the additional logical extension and its exclusive mount:
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
extensions:
|
|
41
|
+
admin:
|
|
42
|
+
version: '1'
|
|
43
|
+
config: {}
|
|
44
|
+
routes:
|
|
45
|
+
/admin/*:
|
|
46
|
+
extension: admin
|
|
47
|
+
methods: [GET, HEAD, POST]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Merge these entries into the existing versioned auth project, then review and update the static project revision pin. In the trusted external host:
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
import {authExtension} from '@jimhoyd/urlcode-auth';
|
|
54
|
+
import {adminExtension} from '@jimhoyd/urlcode-admin';
|
|
55
|
+
|
|
56
|
+
// service, csrfKey and reviewed projectSha256 are operator-owned values.
|
|
57
|
+
export default {
|
|
58
|
+
extensions: [
|
|
59
|
+
authExtension({service, csrfKey, projectSha256}),
|
|
60
|
+
adminExtension({service, csrfKey, projectSha256, authMount: '/account'}),
|
|
61
|
+
],
|
|
62
|
+
async close() { await service.close(); },
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The snippet is an integration fragment; use the auth scaffold's private key/service setup and required HTTPS origin rather than inventing credentials. Use the same CSRF key and shared service. No auth package is loaded from application YAML. Runtime activation uses the explicit external host and matching canonical `--origin`.
|
|
67
|
+
|
|
68
|
+
Administrative actions authenticate internally even without an extra route policy. Missing sessions or administrative permissions receive 404 at the console gate. Use operator role declarations with the actual permissions exported by this implementation: `auth.users.read`, `auth.users.reveal`, `auth.users.export`, `auth.users.manage`, `auth.users.create`, `auth.sessions.manage`, `auth.roles.read`, `auth.audit.read`, `auth.cases.read`, `auth.cases.manage`, and `auth.users.impersonate`. `*` grants full operator-defined administrator permissions. Do not copy the proposal's separate `admin.*` permission names and expect them to work automatically.
|
|
69
|
+
|
|
70
|
+
## Operating the console
|
|
71
|
+
|
|
72
|
+
Bootstrap the first administrator through auth's operator CLI using JSON stdin. Writes require recent authentication and a reason; role/status/session changes run through the auth service's transactional authority checks. Roles themselves remain operator configuration. Masked lists, pagination, permission-filtered navigation and audit records help limit routine exposure.
|
|
73
|
+
|
|
74
|
+
Cases currently model bounded, two-person approval of specific administrative changes with target-version checks. They are not a complete lost-everything identity-verification process. Establish an operator evidence procedure before using factor-reset cases. The console does not supply all proposed notes, close/reject, requester evidence or reporting workflows.
|
|
75
|
+
|
|
76
|
+
Impersonation requires explicit service opt-in, a dedicated permission and a `notifyImpersonation` callback. If the required notification fails, the issued session is revoked before returning it. Impersonation expires and cannot perform fresh account-security/admin changes. The account page shows a warning; arbitrary guest application pages do not automatically show a banner. Ending impersonation requires signing back in as the operator.
|
|
77
|
+
|
|
78
|
+
Optional presentation and invitation/notification callbacks are operator-owned integrations. No real SES, Google or Apple account is provisioned by this package. Keep keys and database backups outside the application project, retain matching configuration, and close the shared service only once after both extensions stop.
|
|
79
|
+
|
|
80
|
+
Apache-2.0. `scripts/pack-sources.mjs` only packs; publication happens exclusively through the tag-driven release workflow.
|
|
81
|
+
|
|
82
|
+
## New local installation
|
|
83
|
+
|
|
84
|
+
After installing the packages (from npm or the reviewed local tarballs), run `urlcode-admin init --directory /absolute/new/site`. It creates a private operator host and database key directory outside the route project, with registration off and auth/admin mounts configured. Follow the generated README to bootstrap the first administrator, configure HTTPS and approve the project revision. This does not deploy or send mail.
|
|
85
|
+
|
|
86
|
+
## Programmatic scaffold
|
|
87
|
+
|
|
88
|
+
`scaffold(request)` is the contract core's `urlcode init --with auth,admin` calls on each installed `@jimhoyd/urlcode-<name>` package; auth and admin export the same shape. It describes admin's contribution and never writes:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import {scaffold} from '@jimhoyd/urlcode-admin';
|
|
92
|
+
const result = await scaffold({directory, project, hostFile, names: ['auth', 'admin']});
|
|
93
|
+
// result.extensions -> {admin: {version: '1', config: {}}}
|
|
94
|
+
// result.routes -> {'/admin/*': {extension: 'admin', methods: ['GET', 'HEAD', 'POST']}}
|
|
95
|
+
// result.hostImports, result.hostSetup, result.hostEntries -> lines for host.mjs
|
|
96
|
+
// result.files -> [] ; result.readme -> "## Administration" section ; result.nextSteps
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Admin contributes the `admin` extension block, the `/admin/*` mount, one `adminExtension({service, csrfKey, projectSha256, authMount: '/account'})` host entry and a README section. It writes no key files and defines no environment: the `service`, `csrfKey` and `projectSha256` identifiers its host entry references are defined by auth's host setup, so `names` must include `auth` (the call refuses otherwise). The caller merges each result's `extensions` and `routes` into one `urlcode.yaml`, concatenates host imports, setup and entries in order, and appends the README sections. `urlcode-admin init` composes this result with auth's initializer and produces the same files it always did. Types `ScaffoldRequest`, `ScaffoldFile` and `ScaffoldResult` are exported.
|
|
100
|
+
|
|
101
|
+
## Private dependency CI
|
|
102
|
+
|
|
103
|
+
Source verification runs automatically for pull requests and pushes to main, and can also be dispatched manually. It checks out the exact core/auth/UI revisions recorded in [`peers.json`](peers.json) (a single workflow step reads the file and later steps use its outputs) and runs Node 22/24/26. `npm test` first runs `scripts/check-sqlite.mjs`, which exits with the SQLite requirement and the bundled version named when the Node release lacks a patched SQLite (3.51.3+, or 3.50.7+/3.44.6+ within those lines), the same rule auth's store enforces at runtime. The approved read-only credentials are `URLCODE_AUTH_READ_TOKEN` and `URLCODE_UI_READ_TOKEN`; deploy keys remain disabled by repository policy. Credentials are not persisted by checkout. Fork pull requests do not receive repository secrets and cannot complete private dependency checkout; they require a reviewed maintainer branch. Do not switch to `pull_request_target` to run untrusted changes with secrets, reuse broad personal tokens, or weaken repository policy. Local full verification and source-package smoke tests remain usable without CI credentials.
|
|
104
|
+
|
|
105
|
+
Releases are separate: pushing a `v<version>` tag whose commit is on `main` and whose version equals `package.json` runs the [release workflow](.github/workflows/release.yml), which installs the three peers from the registry at the lower bound of each declared range, runs the same `npm run verify`, audits production dependencies, packs, attests the tarball, publishes to npm through trusted publishing when the repository variable `PUBLISH_NPM` is `true`, and creates the GitHub release with the tarball attached.
|
|
106
|
+
|
|
107
|
+
### Operator health observations
|
|
108
|
+
|
|
109
|
+
Pass `health: async ({ signal }) => snapshot` to `adminExtension` to expose the
|
|
110
|
+
permission-gated `/admin/health` page (`auth.health.read`). The callback reads your
|
|
111
|
+
trusted runtime/provider monitoring integration; the admin package does not fetch
|
|
112
|
+
project-supplied URLs or reuse management credentials. Its two-second deadline
|
|
113
|
+
aborts the signal, and at most one callback remains in flight even if an adapter
|
|
114
|
+
ignores cancellation. A failed or malformed observation returns an unavailable
|
|
115
|
+
status without exposing the original error.
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
health: async ({ signal }) => ({
|
|
119
|
+
checkedAt: new Date().toISOString(),
|
|
120
|
+
runtime: { status: 'healthy', readiness: 'healthy', version: '0.3.0', routes: 12 },
|
|
121
|
+
sender: 'unknown',
|
|
122
|
+
providers: [{ id: 'google', status: 'unknown' }],
|
|
123
|
+
alerts: [],
|
|
124
|
+
})
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The example shows the shape, not a production probe. Populate it from measured
|
|
128
|
+
operator observations. Status values are `healthy`, `degraded`, `unavailable`, or
|
|
129
|
+
`unknown`. Alert codes are `sender-failed`, `provider-expiring`,
|
|
130
|
+
`presentation-outdated`, and `translation-incomplete`. No provider messages,
|
|
131
|
+
credentials, account identifiers or arbitrary metadata are returned. The timestamp
|
|
132
|
+
makes the age of an observation visible; live provider checks remain a separate
|
|
133
|
+
operator acceptance task.
|
|
134
|
+
|
|
135
|
+
Audit readers with both `auth.audit.read` and `auth.audit.export` can download a
|
|
136
|
+
complete selected UTC range as JSON at `/admin/audit/export?from=...&to=...`.
|
|
137
|
+
Actor, subject and action filters apply to every page. The export rechecks the
|
|
138
|
+
session and permissions while reading and before returning the result. Requests
|
|
139
|
+
above 5,000 events, 4 MiB, or five seconds fail with a request to narrow the range;
|
|
140
|
+
they never silently return a partial file. Export timestamps are bounded at the
|
|
141
|
+
start of the request. Audit retention still limits the available history.
|
|
142
|
+
|
|
143
|
+
The users page supports searches by full or masked email, display name, or account ID; role, status, stored credential method (password, passkey, or external identity), mailbox verification, locale, and UTC creation/activity ranges; and ascending or descending sorting. The verified filter describes mailbox proof. Email-code availability is a deployment setting, not a stored per-user credential method. Text matching uses SQLite's built-in case handling, which is case-insensitive for ASCII letters.
|
|
144
|
+
|
|
145
|
+
Filters and sort order carry through pagination and the bounded, audited CSV page export (at most 50 accounts). Email remains masked in tables, JSON lists and CSV. Pagination is live rather than a database snapshot: if the boundary account is deleted or its sort value changes, restart the search. Cursors contain opaque identifiers and hashes, never full email or display-name sort values. Last-seen values use retained device and session activity, not a complete historical activity log; deleted or expired records can change that view.
|
|
146
|
+
|
|
147
|
+
An account's full email can be revealed only through the explicit **Reveal email address** action, with `auth.users.read` and `auth.users.reveal`, a fresh session, and a reason. The service rechecks the actor's authority and target restrictions and records an audit event. The response remains non-cacheable; routine lists and exports remain masked.
|
|
148
|
+
|
|
149
|
+
### Support-session banner integration
|
|
150
|
+
|
|
151
|
+
Before enabling impersonation, route every application response through
|
|
152
|
+
`withSupportBanner(runtime, { service, authMount: '/account' })` in your trusted
|
|
153
|
+
host. It wraps the `Runtime` returned by core `createRuntime`; your host must call
|
|
154
|
+
the wrapped `handle` for every route. It uses the current auth session to mark HTML
|
|
155
|
+
pages, links to the trusted account page to end the session, disables conditional
|
|
156
|
+
and compressed delivery, and forces no-store on support-session responses. It does
|
|
157
|
+
not send credentials or actor identity to application code.
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
import { createRuntime } from '@jimhoyd/urlcode';
|
|
161
|
+
import { withSupportBanner } from '@jimhoyd/urlcode-admin';
|
|
162
|
+
const runtime = withSupportBanner(
|
|
163
|
+
await createRuntime(project, { origin, extensions }),
|
|
164
|
+
{ service, authMount: '/account' },
|
|
165
|
+
);
|
|
166
|
+
// The host routes every request through runtime.handle(request).
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
HTML that remains compressed, is invalid UTF-8 or exceeds the configured bound
|
|
170
|
+
(default 1 MiB) is replaced with a support-session interstitial. Non-HTML responses
|
|
171
|
+
carry the support marker and no-store headers. Copy can be localized with the
|
|
172
|
+
bounded `message`/`endLabel` options. Use trusted frontend content: arbitrary app
|
|
173
|
+
CSS/JavaScript can hide or alter any DOM notice. Alternate host paths, upstream
|
|
174
|
+
caches and the stock CLI do not install this wrapper automatically; validate the
|
|
175
|
+
actual host integration before turning impersonation on.
|
|
176
|
+
|
|
177
|
+
The user directory offers both current-page CSV and **all matching accounts** CSV. Complete export preserves the selected filters and sort, starts at the beginning, and buffers its result privately: more than 5,000 accounts, 4 MiB or five seconds fails with an instruction to narrow filters, without a partial download. Each included subject passes fresh actor/target export authorization and produces its own audit event; failure can leave those audit events even though no file is returned. Read/export permission is checked again before release. CSV masks email identifiers and escapes formula-like cells. This is live cursor pagination, not a database-wide snapshot: concurrent changes can require restarting and new matching rows can appear or disappear during the operation. A timed-out in-flight trusted service operation may finish auditing, but the helper schedules no further work and returns no data.
|
|
178
|
+
|
|
179
|
+
Account details expose linked overview, method administration, sessions, recovery,
|
|
180
|
+
activity and consent/data sections. Administrator notes are bounded, escaped
|
|
181
|
+
`admin.note` audit events; viewing them requires audit-read authority. Session
|
|
182
|
+
search filters by account, device label and UTC creation range before pagination.
|
|
183
|
+
Method inspection exposes recorded added/last-used timestamps, never provider
|
|
184
|
+
subjects or credential key material. Historical timestamps are shown as unknown.
|
|
185
|
+
|
|
186
|
+
### Integrated host runtime
|
|
187
|
+
|
|
188
|
+
For an embedded host, use `createAdministrationRuntime` instead of constructing
|
|
189
|
+
and wrapping the runtime separately. It mounts auth and admin using the same
|
|
190
|
+
operator service, CSRF key and revision pin, automatically decorates support
|
|
191
|
+
responses, and reports the runtime's observed health/version/route count.
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
import {createAdministrationRuntime} from '@jimhoyd/urlcode-admin';
|
|
195
|
+
|
|
196
|
+
const runtime = await createAdministrationRuntime(projectDirectory, {
|
|
197
|
+
auth: {service, csrfKey, projectSha256},
|
|
198
|
+
runtime: {origin: 'https://accounts.example.com'},
|
|
199
|
+
admin: {notifyImpersonation},
|
|
200
|
+
observations: async ({signal}) => ({
|
|
201
|
+
sender: 'unknown', providers: [], alerts: [],
|
|
202
|
+
}),
|
|
203
|
+
});
|
|
204
|
+
// Forward every request through runtime.handle(), including public app routes.
|
|
205
|
+
// On shutdown: await runtime.close(); await service.close();
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Impersonation still requires the service's explicit opt-in and a successful
|
|
209
|
+
notification callback. The constructor does not open a listening socket, enable
|
|
210
|
+
public registration, probe live providers or change project YAML. Your host owns
|
|
211
|
+
the auth service lifecycle; closing this runtime does not close that shared
|
|
212
|
+
service. Additional trusted extensions can be supplied in `runtime.extensions`.
|
|
213
|
+
Use `admin.authMount` when the auth mount differs from `/account`.
|
|
214
|
+
|
|
215
|
+
The health panel's readiness reflects this runtime's `healthy` state, not an
|
|
216
|
+
external load balancer, database recovery drill or delivery guarantee. Optional
|
|
217
|
+
sender/provider observations pass through the existing bounded, redacted health
|
|
218
|
+
adapter. When omitted those external states remain unknown. The standalone core
|
|
219
|
+
CLI still needs custom host integration for universal support banners; this
|
|
220
|
+
constructor is the supported embedded-host path.
|
|
221
|
+
|
|
222
|
+
## Shared UI dependency
|
|
223
|
+
|
|
224
|
+
Install `@jimhoyd/urlcode-ui` alongside core before installing this package (npm
|
|
225
|
+
does this when all four packages are installed together). The UI peer owns document layout, semantic fields, escaping, themes
|
|
226
|
+
and the locale engine; authentication/administration behavior remains here.
|
|
227
|
+
`scripts/pack-sources.mjs` now requires `--ui /absolute/path/to/urlcode-ui` and
|
|
228
|
+
builds the UI archive before its consumers. Core can use UI without auth/admin.
|
|
229
|
+
Cross-repository source CI needs the narrow `URLCODE_UI_READ_TOKEN`; releases
|
|
230
|
+
resolve the published package instead, and no broad credential is used as a
|
|
231
|
+
workaround.
|
|
232
|
+
|
|
233
|
+
## Presentation
|
|
234
|
+
|
|
235
|
+
Every console screen is an `admin/*` template in the urlcode-ui kit language with a
|
|
236
|
+
declared view model (`adminTemplates`, each with a sample view; `adminUiTemplates` is
|
|
237
|
+
the block the `ui` extension takes): dashboard, users, user-detail, sessions, roles,
|
|
238
|
+
audit, registrations, cases, health, recovery-cases, account-operations, reveal and
|
|
239
|
+
status. The extension computes the view and the template only places it: a template
|
|
240
|
+
cannot change a flow, which permission gates a control, the freshness or reason gate
|
|
241
|
+
on a mutation, what is escaped, or the CSRF field and headers a page sends. Forms,
|
|
242
|
+
table rows, charts and icons arrive in the view as renderer-produced markup built by
|
|
243
|
+
the shared primitives.
|
|
244
|
+
|
|
245
|
+
`adminExtension` takes an optional `ui`, the object `createUiExtension` returns.
|
|
246
|
+
Declare `ui` first in the host file so the runtime activates it before auth and
|
|
247
|
+
admin; admin reads `ui.kit` per request and never captures it at activation.
|
|
248
|
+
|
|
249
|
+
```js
|
|
250
|
+
import { createUiExtension } from '@jimhoyd/urlcode-ui/host';
|
|
251
|
+
import { authExtension, authCatalogue, authUiTemplates } from '@jimhoyd/urlcode-auth';
|
|
252
|
+
import { adminExtension, adminUiTemplates } from '@jimhoyd/urlcode-admin';
|
|
253
|
+
const ui = createUiExtension({ projectSha256, projectRoot: '/absolute/site', sources: [authCatalogue], extensions: [authUiTemplates, adminUiTemplates] });
|
|
254
|
+
export default { extensions: [ui.registration, authExtension({ service, csrfKey, projectSha256, ui }), adminExtension({ service, csrfKey, projectSha256, ui })] };
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
```yaml
|
|
258
|
+
extensions:
|
|
259
|
+
ui: { version: "1", config: { theme: { name: Acme }, templates: ui/templates } }
|
|
260
|
+
routes:
|
|
261
|
+
/assets/ui/*: { extension: ui, methods: [GET, HEAD] }
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
With `ui`, screens render through `ui.kit`: the project's theme, layout, hashed
|
|
265
|
+
stylesheet and copy apply, the console navigation becomes the layout's primary
|
|
266
|
+
navigation and account menu, a project file `ui/templates/admin/<screen>.html`
|
|
267
|
+
shadows the shipped template, and `urlcode-ui doctor` reports every `admin/*`
|
|
268
|
+
template behind its view model. Copy then resolves through the kit's presentation
|
|
269
|
+
composed with the admin catalogue: register `authCatalogue` in `sources` (the kit's
|
|
270
|
+
catalogue holds at most 512 keys, so it cannot also take `adminCatalogue`; admin
|
|
271
|
+
composes its own copy on top) and omit `presentation`. If both are given,
|
|
272
|
+
`presentation` wins.
|
|
273
|
+
|
|
274
|
+
Without `ui`, nothing changes: screens render the same templates through the shared
|
|
275
|
+
primitives inside the console shell, with `presentation` (or the bundled English
|
|
276
|
+
catalogue). The `presentation` option remains the fallback; core plans to retire it
|
|
277
|
+
one minor version after the kit path ships.
|
|
278
|
+
|
|
279
|
+
Use `createAdminPresentation` when translating console-specific copy without the
|
|
280
|
+
kit. It composes bounded auth and admin catalogues while keeping account workflows
|
|
281
|
+
out of URLCode UI. Existing `presentation` instances remain supported; untranslated
|
|
282
|
+
new messages fall back to English.
|
|
283
|
+
|
|
284
|
+
```js
|
|
285
|
+
import {createAdminPresentation} from '@jimhoyd/urlcode-admin';
|
|
286
|
+
const presentation = createAdminPresentation({
|
|
287
|
+
catalogues: {fr: {'adminUi.noSessions': 'Aucune session active.'}},
|
|
288
|
+
});
|
|
289
|
+
// Pass presentation to adminExtension, or the admin options of createAdministrationRuntime.
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
See [UX-REVIEW.md](./UX-REVIEW.md) for reviewed screens, changes and validation limits.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Security boundary
|
|
2
|
+
|
|
3
|
+
The admin console is a privileged client of URLCode auth's service API. Its operator modules, dependencies, service instance, keys and notification transport are trusted. It is not a replacement for the runtime's separately credentialed infrastructure management endpoints, and it does not make a hostile multi-tenant deployment safe.
|
|
4
|
+
|
|
5
|
+
Use the reviewed core extension contract and an explicit static project revision pin. Host modules and database/key files belong outside the application project. Use Node with patched SQLite, HTTPS and a canonical operator-provided origin. Share the auth service and CSRF key intentionally; do not discover plugins or database credentials from project YAML.
|
|
6
|
+
|
|
7
|
+
Same-origin frontend scripts share browser authority. HttpOnly cookies and server guest-header filtering do not prevent such scripts from obtaining CSRF proof and issuing authenticated requests. Auth/admin must run beside trusted frontend content or on a separately isolated origin. Restrictive response CSP does not repair other compromised pages on the same origin.
|
|
8
|
+
|
|
9
|
+
Every delegated mutation must use an actor-token auth service method. UI hiding is not authorization. The service rechecks current privileges, freshness, delegation ceilings, target version/state and last-administrator constraints inside transactions. Do not expose unrestricted operator APIs through generic HTTP dispatch. Account metadata and role names supplied by users never grant permissions.
|
|
10
|
+
|
|
11
|
+
Case approval requires distinct authorized actors and a current target. A two-person approval transaction is not identity proof: establish a documented human evidence procedure for account recovery. Keep meaningful reasons and protect the audit trail. Review permissions before assigning support roles.
|
|
12
|
+
|
|
13
|
+
Impersonation is explicit and bounded, excludes privileged targets, and cannot perform security/admin step-up actions. Notifications are required before returning a usable impersonation session. The built-in account page carries a warning; arbitrary guest pages do not automatically show one. Do not claim universal banners or use impersonation as a substitute for least-privilege diagnostic tooling.
|
|
14
|
+
|
|
15
|
+
Protect database backups, audit exports, operator stdin and notification records as sensitive data. Never post passwords, tokens, keys or live customer database files in public issues. Report vulnerabilities using the repository's private security reporting channel; if none is configured, request a private contact before sharing sensitive evidence.
|
|
16
|
+
|
|
17
|
+
Passing tests do not establish independent assessment, real-provider compatibility, production recovery/soak behavior or WCAG conformance. Track those checks separately. The console's partial reporting and case workflows should not be described as completion of every item in the design proposal.
|
|
18
|
+
|
|
19
|
+
This repository follows the [core URLCode security policy](https://github.com/jimhoyd-com/urlcode/blob/main/SECURITY.md) for reporting and support baseline.
|
package/THREAT-MODEL.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Admin threat model
|
|
2
|
+
|
|
3
|
+
The console is a privileged client of the separate auth service. Its main assets
|
|
4
|
+
are user ownership and access, identifiers, private exports, recovery evidence,
|
|
5
|
+
operator authority and the audit trail. The operator host, installed modules and
|
|
6
|
+
notification integration are trusted; route projects and all request fields are
|
|
7
|
+
untrusted. Run beside trusted frontend content or use an isolated origin.
|
|
8
|
+
|
|
9
|
+
| Threat | Enforced boundary |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| Hidden UI controls bypassed through direct requests | Per-operation permissions plus service-side transactional authorization |
|
|
12
|
+
| Stolen, stale or remembered-device session used for mutations | Fresh real authentication; current roles and account state; impersonation/restricted-session denial |
|
|
13
|
+
| Support operator modifies a more privileged account | Delegation ceiling, self-mutation restrictions and last-administrator checks |
|
|
14
|
+
| Recovery reviewer restores the wrong account or colludes | Evidence recorded for a human procedure, distinct current approvers, target/actor versions, private delivery gate and audit; colluding authorized operators remain a trust risk |
|
|
15
|
+
| Identifier or export leakage | Masked defaults, non-PII cursors, audited reveal/export, no-store responses, bounded authorized exports |
|
|
16
|
+
| HTML, CSV or log injection | Escaped markup, plain evidence, neutralized spreadsheet cells and no raw provider exceptions |
|
|
17
|
+
| Impersonation used as a credential/admin bypass | Explicit opt-in, notice, short expiry, restricted authority, no security step-up |
|
|
18
|
+
| Operator callback stalls or misbehaves | Time/size/concurrency bounds and no activation before required delivery succeeds |
|
|
19
|
+
|
|
20
|
+
Every privileged write must be checked in the auth service's transaction. UI hiding,
|
|
21
|
+
CSRF and a prior role lookup are necessary context but do not replace that check.
|
|
22
|
+
Retain meaningful reasons and limit access to case notes and evidence. A case
|
|
23
|
+
reference is plain text: the console must not fetch uploaded URLs or pretend that
|
|
24
|
+
a two-person approval proves identity automatically.
|
|
25
|
+
|
|
26
|
+
Changes must test denied and racing requests, revocation and stale approvals,
|
|
27
|
+
partial delivery, masked outputs, typed bulk confirmations and all-or-nothing
|
|
28
|
+
operations. Run `npm run verify` and cross-package installed-consumer checks for
|
|
29
|
+
package/initializer changes. Review SECURITY.md and IMPLEMENTATION-STATUS.md.
|
|
30
|
+
|
|
31
|
+
Known deployment limits remain explicit: universal guest-page impersonation banners
|
|
32
|
+
require application integration; live providers and independent security,
|
|
33
|
+
accessibility, recovery and load/soak validation are separate evidence. CI cannot
|
|
34
|
+
establish those properties. No hostile multi-tenant readiness is claimed.
|