@oneuptime/common 12.0.3 → 12.0.4
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/Models/DatabaseModels/CephCluster.ts +14 -0
- package/Models/DatabaseModels/CodeRepository.ts +14 -6
- package/Models/DatabaseModels/DockerHost.ts +14 -0
- package/Models/DatabaseModels/GlobalOidcProject.ts +18 -0
- package/Models/DatabaseModels/GlobalSsoProject.ts +18 -0
- package/Models/DatabaseModels/IoTFleet.ts +14 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +14 -0
- package/Models/DatabaseModels/NetworkDevice.ts +14 -0
- package/Models/DatabaseModels/NetworkInterface.ts +18 -0
- package/Models/DatabaseModels/Project.ts +16 -2
- package/Models/DatabaseModels/ProxmoxCluster.ts +14 -0
- package/Models/DatabaseModels/Service.ts +30 -0
- package/Models/DatabaseModels/StatusPagePrivateUser.ts +30 -0
- package/Models/DatabaseModels/User.ts +30 -0
- package/Server/API/AIAgentDataAPI.ts +31 -0
- package/Server/API/DashboardAPI.ts +419 -12
- package/Server/API/GitHubAPI.ts +119 -283
- package/Server/API/UserAPI.ts +263 -1
- package/Server/EnvironmentConfig.ts +7 -3
- package/Server/Infrastructure/GlobalCache.ts +37 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.ts +21 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.ts +68 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +100 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +214 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.ts +69 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
- package/Server/Services/AnalyticsDatabaseService.ts +72 -1
- package/Server/Services/CodeRepositoryService.ts +105 -2
- package/Server/Services/DatabaseService.ts +185 -2
- package/Server/Services/OpenTelemetryIngestService.ts +230 -26
- package/Server/Services/UserService.ts +184 -0
- package/Server/Utils/AI/Toolbox/CodeTools.ts +45 -4
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +13 -0
- package/Server/Utils/CodeRepository/GitHub/GitHub.ts +157 -4
- package/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.ts +130 -0
- package/Server/Utils/PasswordHash.ts +306 -0
- package/Tests/App/Dashboard/OverviewCustomFields.test.tsx +333 -0
- package/Tests/App/Dashboard/UserCustomFields.test.tsx +392 -0
- package/Tests/Server/API/AIAgentDataRepositoryToken.test.ts +381 -0
- package/Tests/Server/API/DashboardPublicAttributeValuesAPI.test.ts +943 -0
- package/Tests/Server/API/DashboardPublicMetricsAggregateAPI.test.ts +1296 -0
- package/Tests/Server/API/DashboardPublicTemplatePayloads.test.ts +598 -0
- package/Tests/Server/API/GitHubAppInstallationBindingAPI.test.ts +539 -0
- package/Tests/Server/API/Helpers.ts +6 -1
- package/Tests/Server/API/UserProjectsAPI.test.ts +852 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +128 -0
- package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +215 -0
- package/Tests/Server/Services/AddPerUserPasswordSaltMigration.test.ts +178 -0
- package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +582 -0
- package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +5 -0
- package/Tests/Server/Services/CodeRepositoryInstallationBinding.test.ts +305 -0
- package/Tests/Server/Services/CodeRepositoryResolutionBinding.test.ts +147 -0
- package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +870 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +568 -0
- package/Tests/Server/Services/RestoreDroppedUniqueIndexesMigration.test.ts +386 -0
- package/Tests/Server/Services/RestoreServiceLowerNameIndexMigration.test.ts +240 -0
- package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +885 -0
- package/Tests/Server/Services/WidenHashedStringColumnsForScryptMigration.test.ts +220 -0
- package/Tests/Server/Utils/AI/CodeTools.test.ts +47 -1
- package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +10 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +120 -0
- package/Tests/Server/Utils/CodeRepository/GitHubInstallationBinding.test.ts +292 -0
- package/Tests/Server/Utils/GitHubInstallationOwnershipVerification.test.ts +340 -0
- package/Tests/Server/Utils/GitHubWebhookAndTreeCacheIsolation.test.ts +250 -0
- package/Tests/Server/Utils/PasswordHash.test.ts +555 -0
- package/Tests/Types/Database/ColumnLength.test.ts +7 -1
- package/Tests/Types/HashedStringPerUserSalt.test.ts +476 -0
- package/Tests/Types/Monitor/DockerAlertTemplates.test.ts +293 -0
- package/Tests/Types/Monitor/HostAlertTemplates.test.ts +249 -0
- package/Tests/Types/Monitor/IotAlertTemplates.test.ts +320 -0
- package/Tests/Types/Monitor/PodmanAlertTemplates.test.ts +271 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +696 -0
- package/Tests/UI/Utils/UserProjectsModelAPI.test.ts +714 -0
- package/Tests/Utils/TeamMembersByProject.test.ts +655 -0
- package/Types/Database/ColumnLength.ts +9 -1
- package/Types/Database/TableColumn.ts +11 -0
- package/Types/Database/UnsynchronizedIndex.ts +48 -0
- package/Types/HashedString.ts +139 -4
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +85 -11
- package/UI/Components/Navbar/NavBar.tsx +12 -2
- package/UI/Components/Navbar/NavBarMenuModal.tsx +19 -7
- package/UI/Utils/ModelAPI/UserProjectsModelAPI.ts +257 -0
- package/Utils/TeamMembersByProject.ts +237 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CodeRepository.js +14 -6
- package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +16 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +16 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +16 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js +16 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +16 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkInterface.js +16 -1
- package/build/dist/Models/DatabaseModels/NetworkInterface.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +16 -2
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Service.js +28 -1
- package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +32 -0
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +32 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Server/API/AIAgentDataAPI.js +21 -2
- package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +304 -10
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/GitHubAPI.js +94 -167
- package/build/dist/Server/API/GitHubAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +187 -2
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +7 -3
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +29 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js +59 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +93 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js +101 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js +66 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +57 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/CodeRepositoryService.js +90 -2
- package/build/dist/Server/Services/CodeRepositoryService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +160 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +155 -25
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +165 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js +31 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +13 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +126 -4
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js +136 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js.map +1 -0
- package/build/dist/Server/Utils/PasswordHash.js +237 -0
- package/build/dist/Server/Utils/PasswordHash.js.map +1 -0
- package/build/dist/Types/Database/ColumnLength.js +9 -1
- package/build/dist/Types/Database/ColumnLength.js.map +1 -1
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/Database/UnsynchronizedIndex.js +46 -0
- package/build/dist/Types/Database/UnsynchronizedIndex.js.map +1 -0
- package/build/dist/Types/HashedString.js +95 -5
- package/build/dist/Types/HashedString.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +51 -10
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBar.js +3 -1
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +19 -7
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js +156 -0
- package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js.map +1 -0
- package/build/dist/Utils/TeamMembersByProject.js +145 -0
- package/build/dist/Utils/TeamMembersByProject.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import GitHubUtil from "../../../Server/Utils/CodeRepository/GitHub/GitHub";
|
|
2
|
+
import API from "../../../Utils/API";
|
|
3
|
+
import GlobalCache from "../../../Server/Infrastructure/GlobalCache";
|
|
4
|
+
import HTTPResponse from "../../../Types/API/HTTPResponse";
|
|
5
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
6
|
+
import * as crypto from "crypto";
|
|
7
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* Two hardening fixes found while remediating GHSA-xx95-gmcf-7q86. Neither is
|
|
11
|
+
* in the advisory; both sit on the same GitHub App code path.
|
|
12
|
+
*
|
|
13
|
+
* 1. verifyWebhookSignature used to return true when no webhook secret was
|
|
14
|
+
* configured, leaving POST /api/github/webhook unauthenticated on those
|
|
15
|
+
* instances — and its `installation.deleted` branch clears installation IDs
|
|
16
|
+
* across every tenant.
|
|
17
|
+
*
|
|
18
|
+
* 2. getRepositoryTreePaths caches a repository's full private file listing
|
|
19
|
+
* and reads that cache BEFORE minting a token, so the cache key is what
|
|
20
|
+
* stands in for access control on a hit. Keyed on org/repo/branch alone, it
|
|
21
|
+
* could serve one installation's file listing to another.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const WEBHOOK_SECRET: string = "test-webhook-secret";
|
|
25
|
+
|
|
26
|
+
function loadGitHubUtilWithWebhookSecret(
|
|
27
|
+
secret: string | null,
|
|
28
|
+
): typeof GitHubUtil {
|
|
29
|
+
jest.resetModules();
|
|
30
|
+
jest.doMock("../../../Server/EnvironmentConfig", () => {
|
|
31
|
+
return {
|
|
32
|
+
...(jest.requireActual("../../../Server/EnvironmentConfig") as Record<
|
|
33
|
+
string,
|
|
34
|
+
unknown
|
|
35
|
+
>),
|
|
36
|
+
GitHubAppWebhookSecret: secret,
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/*
|
|
41
|
+
* A re-require is the point here: GitHubAppWebhookSecret is read at module
|
|
42
|
+
* load, so the only way to exercise the unconfigured branch is to reload the
|
|
43
|
+
* module against a different mocked config.
|
|
44
|
+
*/
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
|
46
|
+
return require("../../../Server/Utils/CodeRepository/GitHub/GitHub")
|
|
47
|
+
.default as typeof GitHubUtil;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function signPayload(payload: string, secret: string): string {
|
|
51
|
+
return `sha256=${crypto
|
|
52
|
+
.createHmac("sha256", secret)
|
|
53
|
+
.update(payload)
|
|
54
|
+
.digest("hex")}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
describe("GitHub webhook signature verification", () => {
|
|
58
|
+
const payload: string = JSON.stringify({
|
|
59
|
+
action: "deleted",
|
|
60
|
+
installation: { id: 12345678 },
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
jest.resetModules();
|
|
65
|
+
jest.restoreAllMocks();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("accepts a correctly signed payload", () => {
|
|
69
|
+
const util: typeof GitHubUtil =
|
|
70
|
+
loadGitHubUtilWithWebhookSecret(WEBHOOK_SECRET);
|
|
71
|
+
|
|
72
|
+
expect(
|
|
73
|
+
util.verifyWebhookSignature(
|
|
74
|
+
payload,
|
|
75
|
+
signPayload(payload, WEBHOOK_SECRET),
|
|
76
|
+
),
|
|
77
|
+
).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("rejects a payload signed with the wrong secret", () => {
|
|
81
|
+
const util: typeof GitHubUtil =
|
|
82
|
+
loadGitHubUtilWithWebhookSecret(WEBHOOK_SECRET);
|
|
83
|
+
|
|
84
|
+
expect(
|
|
85
|
+
util.verifyWebhookSignature(payload, signPayload(payload, "wrong")),
|
|
86
|
+
).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("rejects a tampered payload", () => {
|
|
90
|
+
const util: typeof GitHubUtil =
|
|
91
|
+
loadGitHubUtilWithWebhookSecret(WEBHOOK_SECRET);
|
|
92
|
+
|
|
93
|
+
const signature: string = signPayload(payload, WEBHOOK_SECRET);
|
|
94
|
+
const tampered: string = JSON.stringify({
|
|
95
|
+
action: "deleted",
|
|
96
|
+
installation: { id: 99999999 },
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(util.verifyWebhookSignature(tampered, signature)).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("rejects a garbage signature without throwing", () => {
|
|
103
|
+
const util: typeof GitHubUtil =
|
|
104
|
+
loadGitHubUtilWithWebhookSecret(WEBHOOK_SECRET);
|
|
105
|
+
|
|
106
|
+
expect(util.verifyWebhookSignature(payload, "not-a-signature")).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
/*
|
|
110
|
+
* The fix. Previously this returned true — so on an instance with no secret
|
|
111
|
+
* configured, anyone who could reach the endpoint could send an
|
|
112
|
+
* `installation.deleted` for any installation id and have OneUptime clear
|
|
113
|
+
* that installation from every project that had it.
|
|
114
|
+
*/
|
|
115
|
+
test("fails CLOSED when no webhook secret is configured", () => {
|
|
116
|
+
const util: typeof GitHubUtil = loadGitHubUtilWithWebhookSecret(null);
|
|
117
|
+
|
|
118
|
+
expect(
|
|
119
|
+
util.verifyWebhookSignature(
|
|
120
|
+
payload,
|
|
121
|
+
signPayload(payload, WEBHOOK_SECRET),
|
|
122
|
+
),
|
|
123
|
+
).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("an unconfigured instance rejects even a well-formed signature", () => {
|
|
127
|
+
const util: typeof GitHubUtil = loadGitHubUtilWithWebhookSecret("");
|
|
128
|
+
|
|
129
|
+
expect(util.verifyWebhookSignature(payload, signPayload(payload, ""))).toBe(
|
|
130
|
+
false,
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe("Repository tree cache is scoped to the installation", () => {
|
|
136
|
+
const TREE_RESPONSE: JSONObject = {
|
|
137
|
+
tree: [
|
|
138
|
+
{ path: "src/secret.ts", type: "blob" },
|
|
139
|
+
{ path: "src", type: "tree" },
|
|
140
|
+
],
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
let getCacheSpy: jest.SpyInstance;
|
|
144
|
+
let setCacheSpy: jest.SpyInstance;
|
|
145
|
+
|
|
146
|
+
beforeEach(() => {
|
|
147
|
+
jest
|
|
148
|
+
.spyOn(GitHubUtil, "getInstallationAccessToken")
|
|
149
|
+
.mockResolvedValue({ token: "ghs_token", expiresAt: new Date() });
|
|
150
|
+
|
|
151
|
+
jest
|
|
152
|
+
.spyOn(API, "get")
|
|
153
|
+
.mockResolvedValue(new HTTPResponse<JSONObject>(200, TREE_RESPONSE, {}));
|
|
154
|
+
|
|
155
|
+
getCacheSpy = jest.spyOn(GlobalCache, "getStringArray");
|
|
156
|
+
getCacheSpy.mockResolvedValue(null);
|
|
157
|
+
|
|
158
|
+
setCacheSpy = jest.spyOn(GlobalCache, "setStringArray");
|
|
159
|
+
setCacheSpy.mockResolvedValue(undefined);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
afterEach(() => {
|
|
163
|
+
jest.restoreAllMocks();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
function argsFor(installationId: string): {
|
|
167
|
+
installationId: string;
|
|
168
|
+
organizationName: string;
|
|
169
|
+
repositoryName: string;
|
|
170
|
+
branchName: string;
|
|
171
|
+
} {
|
|
172
|
+
return {
|
|
173
|
+
installationId: installationId,
|
|
174
|
+
organizationName: "victim-org",
|
|
175
|
+
repositoryName: "private-repo",
|
|
176
|
+
branchName: "main",
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
test("includes the installation id in the cache key", async () => {
|
|
181
|
+
await GitHubUtil.getRepositoryTreePaths(argsFor("11111111"));
|
|
182
|
+
|
|
183
|
+
const cacheKey: string = getCacheSpy.mock.calls[0]![1] as string;
|
|
184
|
+
expect(cacheKey).toContain("11111111");
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
/*
|
|
188
|
+
* The point of the fix: the same org/repo/branch reached through a different
|
|
189
|
+
* installation must not collide, because a hit is served without minting a
|
|
190
|
+
* token or calling GitHub at all.
|
|
191
|
+
*/
|
|
192
|
+
test("two installations naming the same repository use different keys", async () => {
|
|
193
|
+
await GitHubUtil.getRepositoryTreePaths(argsFor("11111111"));
|
|
194
|
+
await GitHubUtil.getRepositoryTreePaths(argsFor("99999999"));
|
|
195
|
+
|
|
196
|
+
const firstKey: string = getCacheSpy.mock.calls[0]![1] as string;
|
|
197
|
+
const secondKey: string = getCacheSpy.mock.calls[1]![1] as string;
|
|
198
|
+
|
|
199
|
+
expect(firstKey).not.toBe(secondKey);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("writes back under the same installation-scoped key it read", async () => {
|
|
203
|
+
await GitHubUtil.getRepositoryTreePaths(argsFor("11111111"));
|
|
204
|
+
|
|
205
|
+
const readKey: string = getCacheSpy.mock.calls[0]![1] as string;
|
|
206
|
+
const writeKey: string = setCacheSpy.mock.calls[0]![1] as string;
|
|
207
|
+
|
|
208
|
+
expect(writeKey).toBe(readKey);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("still distinguishes repositories and branches within one installation", async () => {
|
|
212
|
+
await GitHubUtil.getRepositoryTreePaths({
|
|
213
|
+
installationId: "11111111",
|
|
214
|
+
organizationName: "org",
|
|
215
|
+
repositoryName: "repo-a",
|
|
216
|
+
branchName: "main",
|
|
217
|
+
});
|
|
218
|
+
await GitHubUtil.getRepositoryTreePaths({
|
|
219
|
+
installationId: "11111111",
|
|
220
|
+
organizationName: "org",
|
|
221
|
+
repositoryName: "repo-b",
|
|
222
|
+
branchName: "main",
|
|
223
|
+
});
|
|
224
|
+
await GitHubUtil.getRepositoryTreePaths({
|
|
225
|
+
installationId: "11111111",
|
|
226
|
+
organizationName: "org",
|
|
227
|
+
repositoryName: "repo-a",
|
|
228
|
+
branchName: "develop",
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const keys: Array<string> = getCacheSpy.mock.calls.map(
|
|
232
|
+
(call: Array<unknown>) => {
|
|
233
|
+
return call[1] as string;
|
|
234
|
+
},
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
expect(new Set(keys).size).toBe(3);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test("serves a cache hit without minting a token", async () => {
|
|
241
|
+
getCacheSpy.mockResolvedValue(["src/cached.ts"]);
|
|
242
|
+
|
|
243
|
+
const paths: Array<string> = await GitHubUtil.getRepositoryTreePaths(
|
|
244
|
+
argsFor("11111111"),
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
expect(paths).toEqual(["src/cached.ts"]);
|
|
248
|
+
expect(GitHubUtil.getInstallationAccessToken).not.toHaveBeenCalled();
|
|
249
|
+
});
|
|
250
|
+
});
|