99 entirely between Akrites and Auth0.
1010
1111
12- Packages and Advisories endpoints are implemented. Blast Radius and
13- Contacts are specced separately and not yet built.
12+ Packages, Advisories and Contacts endpoints are implemented. Blast Radius
13+ is specced separately and not yet built.
1414
1515
1616 TODO: scopes below (read:packages, read:stewardships) are the existing
3737 issues it, the implementation reuses read:packages (advisories need no
3838 stewardship read). Confirm the final scope name (read:advisories vs
3939 cdp:advisories:read) with Akrites/product.
40+ - name : Contacts
41+ description : >
42+ Security contacts for a package — includes contact PII (e.g. reporter
43+ emails). The contract gates these behind a dedicated cdp:maintainers:read
44+ scope and forbids reaching them via the packages scope; until Auth0 issues
45+ it, the implementation requires read:maintainer-roles (NOT read:packages).
46+ The response shape is still under discussion upstream (reportingMethods /
47+ reportingGuidelines / integrationHints are reserved and always null today).
4048
4149components :
4250 securitySchemes :
@@ -55,6 +63,7 @@ components:
5563 scopes :
5664 read:packages : Read package detail
5765 read:stewardships : Read package stewardship data
66+ read:maintainer-roles : Read security contacts (interim scope for Contacts; see the Contacts tag)
5867
5968 schemas :
6069 Error :
@@ -356,6 +365,215 @@ components:
356365 allOf :
357366 - $ref : ' #/components/schemas/AdvisoryDetail'
358367
368+ ContactConfidenceBand :
369+ type : string
370+ enum : [PRIMARY, SECONDARY, FALLBACK, NONE]
371+ description : Per-contact confidence — mirrors the internal SecurityContact confidence enum.
372+
373+ OverallConfidenceBand :
374+ type : string
375+ enum : [HIGH, MEDIUM, LOW, NONE]
376+ description : >
377+ Aggregate confidence, derived from the highest-scoring contact.
378+ UNCONFIRMED CROSSWALK: the internal aggregate band is
379+ PRIMARY/SECONDARY/FALLBACK/NONE; this HIGH/MEDIUM/LOW/NONE scale is a
380+ 4→4 tier rename (PRIMARY→HIGH, …) pending confirmation with Akrites —
381+ see OVERALL_CONFIDENCE_CROSSWALK in akritesExternalContactDetail.ts.
382+
383+ SecurityContact :
384+ type : object
385+ required : [channel, value, role, confidenceBand, confidenceScore]
386+ properties :
387+ channel :
388+ type : string
389+ enum : [email, github-pvr, github-handle, url, web-form]
390+ description : Mirrors the internal SecurityContact.channel enum; passed through verbatim.
391+ value :
392+ type : string
393+ example : security@example.org
394+ role :
395+ type : string
396+ enum : [security-team, maintainer, admin, committer, org-owner]
397+ description : Mirrors the internal SecurityContact.role enum; passed through verbatim.
398+ confidenceBand :
399+ $ref : ' #/components/schemas/ContactConfidenceBand'
400+ confidenceScore :
401+ type : number
402+ format : float
403+ minimum : 0
404+ maximum : 1
405+
406+ ReportingMethod :
407+ type : object
408+ nullable : true
409+ description : Reserved shape — always null until SECURITY.md parsing ships.
410+ required : [methodId, type, status, description, actionRequired]
411+ properties :
412+ methodId :
413+ type : integer
414+ type :
415+ type : string
416+ status :
417+ type : string
418+ enum : [preferred, fallback]
419+ recipient :
420+ type : string
421+ nullable : true
422+ description :
423+ type : string
424+ actionRequired :
425+ type : string
426+
427+ ReportingGuidelines :
428+ type : object
429+ nullable : true
430+ description : Reserved shape — always null until SECURITY.md parsing ships.
431+ required : [generalPrinciples, qualityRequirements]
432+ properties :
433+ generalPrinciples :
434+ type : array
435+ items :
436+ type : string
437+ qualityRequirements :
438+ type : object
439+ required : [avoid, recommend]
440+ properties :
441+ avoid :
442+ type : array
443+ items :
444+ type : string
445+ recommend :
446+ type : array
447+ items :
448+ type : object
449+ required : [scenario, action]
450+ properties :
451+ scenario :
452+ type : string
453+ action :
454+ type : string
455+
456+ IntegrationHints :
457+ type : object
458+ nullable : true
459+ description : Reserved shape — always null until computed.
460+ required : [primaryEndpoint, fallbackEndpoint, validationHints]
461+ properties :
462+ primaryEndpoint :
463+ type : object
464+ nullable : true
465+ required : [type, target]
466+ properties :
467+ type :
468+ type : string
469+ target :
470+ type : string
471+ fallbackEndpoint :
472+ type : object
473+ nullable : true
474+ required : [type, target]
475+ properties :
476+ type :
477+ type : string
478+ target :
479+ type : string
480+ validationHints :
481+ type : object
482+ nullable : true
483+ required : [falsePositiveRules, requirePocForSeverity, debounce]
484+ properties :
485+ falsePositiveRules :
486+ type : array
487+ items :
488+ type : string
489+ requirePocForSeverity :
490+ type : array
491+ items :
492+ type : string
493+ debounce :
494+ type : string
495+ nullable : true
496+
497+ ContactDetail :
498+ type : object
499+ description : >
500+ NOTE: shape not final — still under discussion upstream, in particular
501+ how reportingMethods / reportingGuidelines / integrationHints will be
502+ derived from SECURITY.md parsing. Those three, plus targetOrganizationName
503+ and bugBountyProgramFlag, are always null today (not stored). Expect this
504+ schema to change before the contract is frozen.
505+ required :
506+ - purl
507+ - name
508+ - ecosystem
509+ - overallConfidenceBand
510+ - contacts
511+ - securityPolicyUrl
512+ - vulnerabilityReportingUrl
513+ - bugBountyUrl
514+ - pvrEnabled
515+ - targetOrganizationName
516+ - bugBountyProgramFlag
517+ - reportingMethods
518+ - reportingGuidelines
519+ - integrationHints
520+ properties :
521+ purl :
522+ type : string
523+ name :
524+ type : string
525+ ecosystem :
526+ $ref : ' #/components/schemas/Ecosystem'
527+ overallConfidenceBand :
528+ $ref : ' #/components/schemas/OverallConfidenceBand'
529+ contacts :
530+ type : array
531+ items :
532+ $ref : ' #/components/schemas/SecurityContact'
533+ securityPolicyUrl :
534+ type : string
535+ nullable : true
536+ vulnerabilityReportingUrl :
537+ type : string
538+ nullable : true
539+ bugBountyUrl :
540+ type : string
541+ nullable : true
542+ pvrEnabled :
543+ type : boolean
544+ nullable : true
545+ targetOrganizationName :
546+ type : string
547+ nullable : true
548+ description : Reserved — not stored today, always null.
549+ bugBountyProgramFlag :
550+ type : boolean
551+ nullable : true
552+ description : Reserved — not stored today, always null.
553+ reportingMethods :
554+ type : array
555+ nullable : true
556+ items :
557+ $ref : ' #/components/schemas/ReportingMethod'
558+ reportingGuidelines :
559+ $ref : ' #/components/schemas/ReportingGuidelines'
560+ integrationHints :
561+ $ref : ' #/components/schemas/IntegrationHints'
562+
563+ ContactDetailBulkEntry :
564+ type : object
565+ required : [requestedPurl, found, contact]
566+ properties :
567+ requestedPurl :
568+ type : string
569+ found :
570+ type : boolean
571+ contact :
572+ type : object
573+ nullable : true
574+ allOf :
575+ - $ref : ' #/components/schemas/ContactDetail'
576+
359577paths :
360578 /akrites-external/packages/detail :
361579 get :
@@ -560,3 +778,107 @@ paths:
560778 application/json :
561779 schema :
562780 $ref : ' #/components/schemas/Error'
781+
782+ /akrites-external/contacts/detail :
783+ get :
784+ operationId : getContactDetail
785+ summary : Get security contact detail by PURL
786+ description : >
787+ Requires read:maintainer-roles (interim for the contract's cdp:maintainers:read).
788+ Never reachable with the packages scope.
789+ tags : [Contacts]
790+ security :
791+ - M2MBearer :
792+ - read:maintainer-roles
793+ parameters :
794+ - name : purl
795+ in : query
796+ required : true
797+ schema :
798+ type : string
799+ example : pkg:npm/%40angular/core
800+ responses :
801+ ' 200 ' :
802+ description : Security contact detail (empty contacts + NONE band when the package has none).
803+ content :
804+ application/json :
805+ schema :
806+ $ref : ' #/components/schemas/ContactDetail'
807+ ' 400 ' :
808+ description : Malformed purl.
809+ content :
810+ application/json :
811+ schema :
812+ $ref : ' #/components/schemas/Error'
813+ ' 401 ' :
814+ description : Missing or invalid bearer token.
815+ content :
816+ application/json :
817+ schema :
818+ $ref : ' #/components/schemas/Error'
819+ ' 403 ' :
820+ description : Token missing read:maintainer-roles scope.
821+ content :
822+ application/json :
823+ schema :
824+ $ref : ' #/components/schemas/Error'
825+ ' 404 ' :
826+ description : Package not found.
827+ content :
828+ application/json :
829+ schema :
830+ $ref : ' #/components/schemas/Error'
831+
832+ /akrites-external/contacts/detail:batch :
833+ post :
834+ operationId : getContactDetailBatch
835+ summary : Bulk security contact lookup
836+ tags : [Contacts]
837+ security :
838+ - M2MBearer :
839+ - read:maintainer-roles
840+ requestBody :
841+ required : true
842+ content :
843+ application/json :
844+ schema :
845+ type : object
846+ required : [purls]
847+ properties :
848+ purls :
849+ type : array
850+ minItems : 1
851+ maxItems : 100
852+ items :
853+ type : string
854+ responses :
855+ ' 200 ' :
856+ description : Results in the same order as the request.
857+ content :
858+ application/json :
859+ schema :
860+ type : object
861+ required : [results]
862+ properties :
863+ results :
864+ type : array
865+ items :
866+ $ref : ' #/components/schemas/ContactDetailBulkEntry'
867+ ' 400 ' :
868+ description : Validation error (empty array, >100 items, malformed purl).
869+ content :
870+ application/json :
871+ schema :
872+ $ref : ' #/components/schemas/Error'
873+ ' 401 ' :
874+ description : Missing or invalid bearer token.
875+ content :
876+ application/json :
877+ schema :
878+ $ref : ' #/components/schemas/Error'
879+ ' 403 ' :
880+ description : Token missing read:maintainer-roles scope.
881+ content :
882+ application/json :
883+ schema :
884+ $ref : ' #/components/schemas/Error'
0 commit comments