Skip to content

Commit f783383

Browse files
Merge pull request #84 from leanix/feature/CID-3590/picking-up-wrong-file-as-manifest
CID-3590: Skip manifest files with wrong name
2 parents 1337728 + 8a5b9b8 commit f783383

File tree

5 files changed

+252
-6
lines changed

5 files changed

+252
-6
lines changed

src/main/kotlin/net/leanix/githubagent/services/GitHubScanningService.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ class GitHubScanningService(
129129

130130
fun fetchManifestFilesAndSend(installation: Installation, repository: RepositoryDto) {
131131
if (repository.archived) return
132-
val manifestFiles = fetchManifestFiles(installation, repository.name).getOrThrow().items
132+
val manifestFiles = fetchManifestFiles(installation, repository.name).getOrThrow()
133133
val manifestFilesContents = fetchManifestContents(
134134
installation,
135135
manifestFiles,
@@ -149,13 +149,14 @@ class GitHubScanningService(
149149

150150
private fun fetchManifestFiles(installation: Installation, repositoryName: String) = runCatching {
151151
val installationToken = cachingService.get("installationToken:${installation.id}").toString()
152-
rateLimitHandler.executeWithRateLimitHandler(RateLimitType.SEARCH) {
152+
val manifestFiles = rateLimitHandler.executeWithRateLimitHandler(RateLimitType.SEARCH) {
153153
gitHubClient.searchManifestFiles(
154154
"Bearer $installationToken",
155155
"" +
156156
"repo:${installation.account.login}/$repositoryName filename:$MANIFEST_FILE_NAME"
157157
)
158158
}
159+
manifestFiles.items.filter { it.name.lowercase() == MANIFEST_FILE_NAME }
159160
}
160161
private fun fetchManifestContents(
161162
installation: Installation,

src/main/kotlin/net/leanix/githubagent/services/WebhookEventService.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ class WebhookEventService(
110110
repositoryName: String,
111111
installationToken: String
112112
) {
113-
val addedManifestFiles = headCommit.added.filter { it.contains(MANIFEST_FILE_NAME) }
114-
val modifiedManifestFiles = headCommit.modified.filter { it.contains(MANIFEST_FILE_NAME) }
115-
val removedManifestFiles = headCommit.removed.filter { it.contains(MANIFEST_FILE_NAME) }
113+
val addedManifestFiles = headCommit.added.filter { isLeanixManifestFile(it.lowercase()) }
114+
val modifiedManifestFiles = headCommit.modified.filter { isLeanixManifestFile(it.lowercase()) }
115+
val removedManifestFiles = headCommit.removed.filter { isLeanixManifestFile(it.lowercase()) }
116116

117117
addedManifestFiles.forEach { filePath ->
118118
handleAddedOrModifiedManifestFile(
@@ -143,6 +143,8 @@ class WebhookEventService(
143143
}
144144
}
145145

146+
private fun isLeanixManifestFile(it: String) = it == MANIFEST_FILE_NAME || it.endsWith("/$MANIFEST_FILE_NAME")
147+
146148
private fun getInstallationToken(installationId: Int): String {
147149
var installationToken = cachingService.get("installationToken:$installationId")?.toString()
148150
if (installationToken == null) {

src/main/kotlin/net/leanix/githubagent/shared/Constants.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ val SUPPORTED_EVENT_TYPES = listOf(
1212
"INSTALLATION",
1313
)
1414

15-
val fileNameMatchRegex = Regex("/?$MANIFEST_FILE_NAME\$")
15+
val fileNameMatchRegex = Regex("/?$MANIFEST_FILE_NAME\$", RegexOption.IGNORE_CASE)
1616

1717
const val GITHUB_APP_LABEL = "GitHub App"
1818
const val INSTALLATION_LABEL = "Installation"

src/test/kotlin/net/leanix/githubagent/services/GitHubScanningServiceTest.kt

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,147 @@ class GitHubScanningServiceTest {
288288
assertEquals(fileSlot.captured.manifestFiles[0].path, "")
289289
}
290290

291+
@Test
292+
fun `scanGitHubResources should not send manifest files over WebSocket for manifest files with wrong name`() {
293+
// given
294+
every { cachingService.get("runId") } returns runId
295+
every { gitHubGraphQLService.getRepositories(any(), any()) } returns PagedRepositories(
296+
repositories = listOf(
297+
RepositoryDto(
298+
id = "repo1",
299+
name = "TestRepo",
300+
organizationName = "testOrg",
301+
description = "A test repository",
302+
url = "https://github.com/testRepo",
303+
defaultBranch = "main",
304+
archived = false,
305+
visibility = RepositoryVisibility.PUBLIC,
306+
updatedAt = "2024-01-01T00:00:00Z",
307+
languages = listOf("Kotlin", "Java"),
308+
topics = listOf("test", "example"),
309+
)
310+
),
311+
hasNextPage = false,
312+
cursor = null
313+
)
314+
every { gitHubClient.searchManifestFiles(any(), any()) } returns GitHubSearchResponse(
315+
1,
316+
listOf(
317+
ItemResponse(
318+
name = MANIFEST_FILE_NAME,
319+
path = MANIFEST_FILE_NAME,
320+
repository = RepositoryItemResponse(
321+
name = "TestRepo",
322+
fullName = "testOrg/TestRepo"
323+
),
324+
url = "http://url"
325+
),
326+
ItemResponse(
327+
name = MANIFEST_FILE_NAME,
328+
path = "a/$MANIFEST_FILE_NAME",
329+
repository = RepositoryItemResponse(
330+
name = "TestRepo",
331+
fullName = "testOrg/TestRepo"
332+
),
333+
url = "http://url"
334+
),
335+
ItemResponse(
336+
name = "a-$MANIFEST_FILE_NAME",
337+
path = "a/a-$MANIFEST_FILE_NAME",
338+
repository = RepositoryItemResponse(
339+
name = "TestRepo",
340+
fullName = "testOrg/TestRepo"
341+
),
342+
url = "http://url"
343+
)
344+
)
345+
)
346+
every { gitHubGraphQLService.getManifestFileContent(any(), any(), MANIFEST_FILE_NAME, any()) } returns "content"
347+
every {
348+
gitHubGraphQLService.getManifestFileContent(any(), any(), "a/$MANIFEST_FILE_NAME", any())
349+
} returns "content"
350+
351+
// when
352+
gitHubScanningService.scanGitHubResources()
353+
354+
// then
355+
verify(exactly = 1) { webSocketService.sendMessage(eq("$runId/manifestFiles"), any()) }
356+
verify(exactly = 1) { syncLogService.sendInfoLog("Scanning repository TestRepo for manifest files.") }
357+
verify(exactly = 1) { syncLogService.sendInfoLog("Found 2 manifest files in repository TestRepo.") }
358+
}
359+
360+
@Test
361+
fun `scanGitHubResources should accept manifest files with case ignored`() {
362+
// given
363+
every { cachingService.get("runId") } returns runId
364+
every { gitHubGraphQLService.getRepositories(any(), any()) } returns PagedRepositories(
365+
repositories = listOf(
366+
RepositoryDto(
367+
id = "repo1",
368+
name = "TestRepo",
369+
organizationName = "testOrg",
370+
description = "A test repository",
371+
url = "https://github.com/testRepo",
372+
defaultBranch = "main",
373+
archived = false,
374+
visibility = RepositoryVisibility.PUBLIC,
375+
updatedAt = "2024-01-01T00:00:00Z",
376+
languages = listOf("Kotlin", "Java"),
377+
topics = listOf("test", "example"),
378+
)
379+
),
380+
hasNextPage = false,
381+
cursor = null
382+
)
383+
every { gitHubClient.searchManifestFiles(any(), any()) } returns GitHubSearchResponse(
384+
1,
385+
listOf(
386+
ItemResponse(
387+
name = "leanIX.yaml",
388+
path = "leanIX.yaml",
389+
repository = RepositoryItemResponse(
390+
name = "TestRepo",
391+
fullName = "testOrg/TestRepo"
392+
),
393+
url = "http://url"
394+
),
395+
ItemResponse(
396+
name = "lEAnIX.yaml",
397+
path = "a/lEAnIX.yaml",
398+
repository = RepositoryItemResponse(
399+
name = "TestRepo",
400+
fullName = "testOrg/TestRepo"
401+
),
402+
url = "http://url"
403+
),
404+
ItemResponse(
405+
name = MANIFEST_FILE_NAME,
406+
path = "b/$MANIFEST_FILE_NAME",
407+
repository = RepositoryItemResponse(
408+
name = "TestRepo",
409+
fullName = "testOrg/TestRepo"
410+
),
411+
url = "http://url"
412+
)
413+
)
414+
)
415+
every {
416+
gitHubGraphQLService.getManifestFileContent(any(), any(), "b/leanix.yaml", any())
417+
} returns "content"
418+
every {
419+
gitHubGraphQLService.getManifestFileContent(any(), any(), "leanIX.yaml", any())
420+
} returns "content"
421+
every {
422+
gitHubGraphQLService.getManifestFileContent(any(), any(), "a/lEAnIX.yaml", any())
423+
} returns "content"
424+
425+
// when
426+
gitHubScanningService.scanGitHubResources()
427+
428+
// then
429+
verify(exactly = 1) { syncLogService.sendInfoLog("Found 3 manifest files in repository TestRepo.") }
430+
}
431+
291432
@Test
292433
fun `scanGitHubResources should skip organizations without correct permissions and events`() {
293434
every { cachingService.get("runId") } returns runId

src/test/kotlin/net/leanix/githubagent/services/WebhookEventServiceTest.kt

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,108 @@ class WebhookEventServiceTest {
118118
}
119119
}
120120

121+
@Test
122+
fun `should not process push event with wrong name`() {
123+
val payload = """{
124+
"repository": {
125+
"name": "repo",
126+
"full_name": "owner/repo",
127+
"owner": {"name": "owner"},
128+
"default_branch": "main"
129+
},
130+
"head_commit": {
131+
"added": [],
132+
"modified": ["$MANIFEST_FILE_NAME", "a-$MANIFEST_FILE_NAME", "a/$MANIFEST_FILE_NAME", "a/a-$MANIFEST_FILE_NAME"],
133+
"removed": []
134+
},
135+
"installation": {"id": 1},
136+
"ref": "refs/heads/main"
137+
}"""
138+
139+
webhookEventService.consumeWebhookEvent("PUSH", payload)
140+
141+
verify(exactly = 1) {
142+
webSocketService.sendMessage(
143+
"/events/manifestFile",
144+
ManifestFileUpdateDto(
145+
"owner/repo",
146+
ManifestFileAction.MODIFIED,
147+
"content",
148+
""
149+
)
150+
)
151+
}
152+
153+
verify(exactly = 1) {
154+
webSocketService.sendMessage(
155+
"/events/manifestFile",
156+
ManifestFileUpdateDto(
157+
"owner/repo",
158+
ManifestFileAction.MODIFIED,
159+
"content",
160+
"tree/main/a"
161+
)
162+
)
163+
}
164+
}
165+
166+
@Test
167+
fun `should accept manifest files with case ignore`() {
168+
val payload = """{
169+
"repository": {
170+
"name": "repo",
171+
"full_name": "owner/repo",
172+
"owner": {"name": "owner"},
173+
"default_branch": "main"
174+
},
175+
"head_commit": {
176+
"added": [],
177+
"modified": ["a/$MANIFEST_FILE_NAME", "b/leanIX.yaml", "LEanIX.yaml"],
178+
"removed": []
179+
},
180+
"installation": {"id": 1},
181+
"ref": "refs/heads/main"
182+
}"""
183+
184+
webhookEventService.consumeWebhookEvent("PUSH", payload)
185+
186+
verify(exactly = 1) {
187+
webSocketService.sendMessage(
188+
"/events/manifestFile",
189+
ManifestFileUpdateDto(
190+
"owner/repo",
191+
ManifestFileAction.MODIFIED,
192+
"content",
193+
"tree/main/b"
194+
)
195+
)
196+
}
197+
198+
verify(exactly = 1) {
199+
webSocketService.sendMessage(
200+
"/events/manifestFile",
201+
ManifestFileUpdateDto(
202+
"owner/repo",
203+
ManifestFileAction.MODIFIED,
204+
"content",
205+
"tree/main/a"
206+
)
207+
)
208+
}
209+
210+
verify(exactly = 1) {
211+
webSocketService.sendMessage(
212+
"/events/manifestFile",
213+
ManifestFileUpdateDto(
214+
"owner/repo",
215+
ManifestFileAction.MODIFIED,
216+
"content",
217+
""
218+
)
219+
)
220+
}
221+
}
222+
121223
@Test
122224
fun `should send all events of type other than push to backend without processing`() {
123225
val payload = """{

0 commit comments

Comments
 (0)