Skip to content

Commit 085a382

Browse files
committed
feat: resolve maven developer ids via github contributor verification
Signed-off-by: Mouad BANI <mouad-mb@outlook.com>
1 parent cc3afcd commit 085a382

1 file changed

Lines changed: 40 additions & 7 deletions

File tree

  • services/apps/packages_worker/src/security-contacts/extractors/registry

services/apps/packages_worker/src/security-contacts/extractors/registry/maven.ts

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { XMLParser } from 'fast-xml-parser'
33
import { ExtractorResult, ProvenanceEntry, RawContact } from '../../types'
44
import { fetchText, isEmail, registryHeaders } from '../http'
55

6+
import { toHandleCandidates } from './handles'
67
import { ParsedPurl } from './purl'
78

89
const SOURCE = 'maven-pom'
@@ -74,6 +75,23 @@ function mapProjectDevelopers(project: any, sourceUrl: string, fetchedAt: string
7475
return contacts
7576
}
7677

78+
// Developer <id>s are frequently GitHub logins but Central does not guarantee it (Apache ids
79+
// are LDAP usernames) — emitted as candidates for repo-contributor corroboration, and only for
80+
// developers that did not already yield an email contact.
81+
function mapProjectDeveloperHandles(
82+
project: any,
83+
sourceUrl: string,
84+
fetchedAt: string,
85+
): RawContact[] {
86+
const handles: unknown[] = []
87+
for (const dev of asArray(project.developers?.developer)) {
88+
const raw = (dev as any)?.email
89+
if (typeof raw === 'string' && deobfuscateEmail(raw)) continue
90+
handles.push((dev as any)?.id)
91+
}
92+
return toHandleCandidates(handles, SOURCE, sourceUrl, fetchedAt)
93+
}
94+
7795
export function mapMavenPom(xml: string, sourceUrl: string, fetchedAt: string): RawContact[] {
7896
const project = parseProject(xml)
7997
if (!project) return []
@@ -162,6 +180,7 @@ function scmOwners(project: any): string[] {
162180
interface ParentPom {
163181
group: string
164182
contacts: RawContact[]
183+
handles: RawContact[]
165184
owners: string[]
166185
parent: ParentRef | null
167186
}
@@ -181,9 +200,11 @@ async function fetchParentPom(
181200
if (!text) return null
182201
const project = parseProject(text)
183202
if (!project) return null
203+
const fetchedAt = new Date().toISOString()
184204
return {
185205
group: typeof project.groupId === 'string' ? project.groupId : ref.group,
186-
contacts: mapProjectDevelopers(project, url, new Date().toISOString()),
206+
contacts: mapProjectDevelopers(project, url, fetchedAt),
207+
handles: mapProjectDeveloperHandles(project, url, fetchedAt),
187208
owners: scmOwners(project),
188209
parent: parentRef(project),
189210
}
@@ -212,8 +233,9 @@ async function traverseParents(
212233
repoUrl: string | undefined,
213234
timeoutMs: number,
214235
userAgent: string,
215-
): Promise<RawContact[]> {
236+
): Promise<{ contacts: RawContact[]; handleCandidates: RawContact[] }> {
216237
const repoOwner = repoUrl ? repoOwnerOf(repoUrl) : null
238+
const handleCandidates: RawContact[] = []
217239
const seen = new Set<string>()
218240
let ref = parentRef(leafProject)
219241
for (let depth = 0; ref !== null && depth < MAX_PARENT_DEPTH; depth++) {
@@ -225,10 +247,11 @@ async function traverseParents(
225247
const related =
226248
groupRelated(leafGroup, pom.group) || (repoOwner !== null && pom.owners.includes(repoOwner))
227249
if (!related) break
228-
if (pom.contacts.length > 0) return pom.contacts
250+
handleCandidates.push(...pom.handles)
251+
if (pom.contacts.length > 0) return { contacts: pom.contacts, handleCandidates }
229252
ref = pom.parent
230253
}
231-
return []
254+
return { contacts: [], handleCandidates }
232255
}
233256

234257
export async function fetchMaven(
@@ -250,9 +273,19 @@ export async function fetchMaven(
250273
const project = parseProject(text)
251274
if (!project) return { contacts: [], policies: {} }
252275

253-
let contacts = mapProjectDevelopers(project, url, new Date().toISOString())
276+
const fetchedAt = new Date().toISOString()
277+
let contacts = mapProjectDevelopers(project, url, fetchedAt)
278+
const handleCandidates = mapProjectDeveloperHandles(project, url, fetchedAt)
254279
if (contacts.length === 0) {
255-
contacts = await traverseParents(project, parsed.namespace, repoUrl, timeoutMs, userAgent)
280+
const inherited = await traverseParents(
281+
project,
282+
parsed.namespace,
283+
repoUrl,
284+
timeoutMs,
285+
userAgent,
286+
)
287+
contacts = inherited.contacts
288+
handleCandidates.push(...inherited.handleCandidates)
256289
}
257-
return { contacts, policies: {} }
290+
return { contacts, policies: {}, handleCandidates }
258291
}

0 commit comments

Comments
 (0)