@app-vitals/agent-admin 1.13.0 → 1.15.0

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.
@@ -124,6 +124,7 @@ describe("getInstallationToken", () => {
124
124
  token: "ghs_cassetteInstallationToken0000000000",
125
125
  expiresAt: "2026-11-09T13:00:00Z",
126
126
  installationId: 4242,
127
+ permissions: { enterprise_organization_installations: "write", metadata: "read" },
127
128
  });
128
129
  expect(calls.map((c) => `${c.method} ${c.path}`)).toEqual([ENT, MINT]);
129
130
  for (const c of calls) {
@@ -157,6 +157,8 @@ export interface EnterpriseInstallationToken {
157
157
  token: string;
158
158
  expiresAt: string;
159
159
  installationId: number;
160
+ /** Permissions granted to the minted token, as reported by GitHub (when present). */
161
+ permissions?: Record<string, string>;
160
162
  }
161
163
 
162
164
  export interface GithubEnterpriseClientOptions {
@@ -220,7 +222,11 @@ export class GithubEnterpriseClient {
220
222
  "GitHub enterprise installation lookup: response missing id",
221
223
  );
222
224
  }
223
- const minted = await this.request<{ token?: string; expires_at?: string }>(
225
+ const minted = await this.request<{
226
+ token?: string;
227
+ expires_at?: string;
228
+ permissions?: Record<string, string>;
229
+ }>(
224
230
  "POST",
225
231
  `/app/installations/${inst.data.id}/access_tokens`,
226
232
  jwt,
@@ -236,6 +242,9 @@ export class GithubEnterpriseClient {
236
242
  token: minted.data.token,
237
243
  expiresAt: minted.data.expires_at,
238
244
  installationId: inst.data.id,
245
+ ...(minted.data.permissions && typeof minted.data.permissions === "object"
246
+ ? { permissions: minted.data.permissions }
247
+ : {}),
239
248
  };
240
249
  return this.cached;
241
250
  }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * admin/src/github-enterprise-isolation.unit.test.ts
3
+ * Isolation exception guard (GHE-1.3): the GitHub Enterprise App private key
4
+ * is reachable only from the enterprise service, its admin API and the boot
5
+ * wiring. Agent config bundle, agent-bearer routes and env serialization must
6
+ * never import the service, touch its table or call getPrivateKey().
7
+ */
8
+
9
+ import { describe, expect, it } from "bun:test";
10
+ import { readdirSync, readFileSync, statSync } from "node:fs";
11
+ import { join, relative } from "node:path";
12
+
13
+ const REPO = new URL("../../", import.meta.url).pathname;
14
+
15
+ /** Non-test source files that may reference the key-bearing service/table. */
16
+ const ALLOWED = new Set([
17
+ "admin/src/github-enterprise-apps.ts",
18
+ "admin/src/github-enterprise-api.ts",
19
+ "admin/src/admin-boot.ts",
20
+ ]);
21
+
22
+ function sources(dir: string, out: string[] = []): string[] {
23
+ for (const name of readdirSync(dir)) {
24
+ if (name === "node_modules" || name === "client" || name.startsWith(".")) {
25
+ continue;
26
+ }
27
+ const p = join(dir, name);
28
+ if (statSync(p).isDirectory()) sources(p, out);
29
+ else if (
30
+ /\.(ts|tsx)$/.test(name) &&
31
+ !/\.(unit|integration|smoke|fixtures)\.test\.ts$|\.test\.ts$|\.fixtures\.ts$/.test(
32
+ name,
33
+ )
34
+ ) {
35
+ out.push(p);
36
+ }
37
+ }
38
+ return out;
39
+ }
40
+
41
+ describe("GitHub Enterprise key isolation", () => {
42
+ const files = ["admin/src", "runtime", "lib", "chat", "index.ts"].flatMap(
43
+ (d) => {
44
+ const p = join(REPO, d);
45
+ return statSync(p).isDirectory() ? sources(p) : [p];
46
+ },
47
+ );
48
+
49
+ it("scans a non-trivial set of files", () => {
50
+ expect(files.length).toBeGreaterThan(50);
51
+ });
52
+
53
+ it("only the allowlisted modules touch the key-bearing service or table", () => {
54
+ const offenders = files
55
+ .map((f) => relative(REPO, f))
56
+ .filter((rel) => !ALLOWED.has(rel))
57
+ .filter((rel) => {
58
+ const src = readFileSync(join(REPO, rel), "utf-8");
59
+ return (
60
+ /github-enterprise-apps/.test(src) ||
61
+ /gitHubEnterpriseApp\b/.test(src) ||
62
+ /GitHubEnterpriseApp\b/.test(src) ||
63
+ /privateKeyEncrypted/.test(src)
64
+ );
65
+ });
66
+ expect(offenders).toEqual([]);
67
+ });
68
+
69
+ it("getPrivateKey() is only called by the enterprise API", () => {
70
+ const callers = files
71
+ .map((f) => relative(REPO, f))
72
+ .filter((rel) =>
73
+ /\.getPrivateKey\(/.test(readFileSync(join(REPO, rel), "utf-8")),
74
+ );
75
+ expect(callers).toEqual(["admin/src/github-enterprise-api.ts"]);
76
+ });
77
+
78
+ it("the config bundle and env serializers never read the enterprise table", () => {
79
+ for (const rel of ["admin/src/agent-envs.ts", "admin/src/api.ts"]) {
80
+ const src = readFileSync(join(REPO, rel), "utf-8");
81
+ expect(src).not.toMatch(/enterprise/i);
82
+ }
83
+ });
84
+ });