Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
3fcdf7e
fixing a read after write issue.
david-rocca Mar 12, 2026
5a90ced
whoops
david-rocca Mar 12, 2026
1df06cb
Merge pull request #1675 from CVEProject/dr_1665
david-rocca Mar 13, 2026
f5420a6
Fixing an issue were ADMIN was not fully removed
david-rocca Mar 16, 2026
5b660ff
not passing authority is now a no no
david-rocca Mar 16, 2026
1d3f714
First pass at conversation edits
cberger8 Mar 17, 2026
531758e
If the user is a secretariat, they will be able to see the visibility
david-rocca Mar 17, 2026
e10de23
Merge pull request #1693 from CVEProject/dr_conversation_visibility_u…
david-rocca Mar 17, 2026
72a5324
Merge branch 'dev' into dr_1685
david-rocca Mar 17, 2026
7d05dc7
Merge branch 'dev' into dr_1677
david-rocca Mar 17, 2026
a55b84e
syntax fixes
emathew5 Mar 17, 2026
73f3356
Merge branch 'dev' into emathew/1669_and_1668
emathew5 Mar 17, 2026
c487a9e
Do not allow arrays
david-rocca Mar 17, 2026
5d23372
add 'username' to body parameters
emathew5 Mar 17, 2026
a3817ec
undo changes
emathew5 Mar 17, 2026
976393c
revert change
emathew5 Mar 17, 2026
9da4e15
Merge pull request #1695 from CVEProject/dr_1694
david-rocca Mar 17, 2026
da7d627
fix delete user bugs
emathew5 Mar 17, 2026
d403999
Merge branch 'dev' into dr_1677
jdaigneau5 Mar 17, 2026
9191c3c
Merge pull request #1686 from CVEProject/dr_1677
jdaigneau5 Mar 17, 2026
b1feff8
Merge branch 'dev' into dr_1685
jdaigneau5 Mar 17, 2026
655aecb
Merge pull request #1689 from CVEProject/dr_1685
jdaigneau5 Mar 17, 2026
b7bf18e
Merge branch 'dev' into emathew/1669_and_1668
david-rocca Mar 18, 2026
bc43606
Conflicts
david-rocca Mar 18, 2026
552f325
getOrgIdQuota no longer uses sessions
david-rocca Mar 13, 2026
829d3d2
Added missing causal consistency flag
david-rocca Mar 13, 2026
ef84a41
Updated the audit repo to not require another get.
david-rocca Mar 13, 2026
a5e3f8c
Fixing a read after write during add a user to the org list of uuids
david-rocca Mar 13, 2026
0cb5fad
conflicts
david-rocca Mar 18, 2026
4dc924d
more conflicts
david-rocca Mar 18, 2026
6367150
Saving a file is hard
david-rocca Mar 18, 2026
2051c06
Merge pull request #1676 from CVEProject/dr_1674
jdaigneau5 Mar 18, 2026
25c922f
Merge branch 'dev' into dr_1683
david-rocca Mar 18, 2026
d3df80d
Merge pull request #1691 from CVEProject/dr_1683
jdaigneau5 Mar 18, 2026
121e340
Merge branch 'dev' into emathew/1669_and_1668
david-rocca Mar 18, 2026
4332b40
Merge pull request #1696 from CVEProject/emathew/1669_and_1668
david-rocca Mar 18, 2026
db1f57c
conflict fixes
david-rocca Mar 18, 2026
5ab63ef
fix wrongly named var
david-rocca Mar 18, 2026
298746f
testing something fun
david-rocca Mar 18, 2026
315952d
Current updated code
david-rocca Mar 25, 2026
6b51b16
returning old inuse
david-rocca Mar 25, 2026
0f50efb
resolving all high vulns
david-rocca Mar 25, 2026
b864843
removing all vulns
david-rocca Mar 25, 2026
d153df8
one day I will stop doing this
david-rocca Mar 25, 2026
ab0b409
resolving bug
david-rocca Mar 25, 2026
aebde3e
Merge pull request #1698 from CVEProject/dr_1679
david-rocca Mar 26, 2026
5694239
Merging dev
cberger8 Mar 30, 2026
cd1d361
Expose conversation edit endpoint and add integration tests
cberger8 Mar 31, 2026
1b5b30e
Merging
cberger8 Mar 31, 2026
0819d1c
Merge branch 'dev' into dr_1664
jdaigneau5 Mar 31, 2026
df60ddb
remove duplicated code
cberger8 Mar 31, 2026
74bc27e
Merge pull request #1703 from CVEProject/dr_1664
jdaigneau5 Mar 31, 2026
5b1803b
fix docs
cberger8 Mar 31, 2026
24e4fbb
Merge branch 'dev' into cb_1612_conversation_updates
cberger8 Mar 31, 2026
6a65e69
Merge pull request #1692 from CVEProject/cb_1612_conversation_updates
david-rocca Mar 31, 2026
baec8e8
Removing the extra fields, fixing populate.js
david-rocca Mar 30, 2026
127d4f8
Merge pull request #1710 from CVEProject/dr_1678
jdaigneau5 Apr 1, 2026
2cca5a9
Locking down endpoint
david-rocca Apr 1, 2026
4a2ef2f
Merge branch 'dev' of https://github.com/CVEProject/cve-services into…
david-rocca Apr 1, 2026
20538fc
Updating build number
david-rocca Apr 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions api-docs/openapi.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"openapi": "3.0.2",
"info": {
"version": "2.7.0",
"info": {
"version": "2.7.1",
"title": "CVE Services API",
"description": "The CVE Services API supports automation tooling for the CVE Program. Credentials are required for most service endpoints. Representatives of <a href='https://www.cve.org/ProgramOrganization/CNAs'>CVE Numbering Authorities (CNAs)</a> should use one of the methods below to obtain credentials: <ul><li>If your organization already has an Organizational Administrator (OA) account for the CVE Services, ask your admin for credentials</li> <li>Contact your Root (<a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/Google'>Google</a>, <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/INCIBE'>INCIBE</a>, <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/jpcert'>JPCERT/CC</a>, or <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/redhat'>Red Hat</a>) or Top-Level Root (<a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/icscert'>CISA ICS</a> or <a href='https://www.cve.org/PartnerInformation/ListofPartners/partner/mitre'>MITRE</a>) to request credentials </ul> <p>CVE data is to be in the JSON 5.2 CVE Record format. Details of the JSON 5.2 schema are located <a href='https://github.com/CVEProject/cve-schema/releases/tag/v5.2.0' target='_blank'>here</a>.</p> <a href='https://cveform.mitre.org/' class='link' target='_blank'>Contact the CVE Services team</a>",
"contact": {
Expand Down Expand Up @@ -2605,7 +2605,7 @@
"Registry Organization"
],
"summary": "Updates information about the organization specified by short name (accessible Temporarily to Secretariat only)",
"description": " <h2>Access Control</h2> <p>User must belong to an organization with the <b>Secretariat</b> role temporarily.</p> <p>In the future, only the organization's admin will be able to request changes to its information.</p> <p>With Joint Approval required for the following fields:</p> <h2>Expected Behavior</h2> <b>This endpoint expects a full organization object in the request body.</b> <p><b>Secretariat:</b> Updates any organization's information</p> <p><b>Organization Admin:</b> Requests changes to its organization's information</p> <ul> <li>short_name</li> <li>long_name</li> <li>authority</li> <li>aliases</li> <li>oversees</li> <li>root_or_tlr</li> <li>charter_or</li> <li>product_list</li> <li>disclosure_policy</li> <li>contact_info.poc</li> <li>contact_info.poc_email</li> <li>contact_info.poc_phone</li> <li>contact_info.org_email</li> <li>cna_role_type</li> <li>cna_country</li> <li>vulnerability_advisory_locations</li> <li>advisory_location_require_credentials</li> <li>industry</li> <li>tl_root_start_date</li> <li>is_cna_discussion_list</li> </ul>",
"description": " <h2>Access Control</h2> <p>User must belong to an organization with the <b>Secretariat</b> role temporarily.</p> <p>In the future, only the organization's admin will be able to request changes to its information.</p> <p>With Joint Approval required for the following fields:</p> <h2>Expected Behavior</h2> <b>This endpoint expects a full organization object in the request body.</b> <p><b>Secretariat:</b> Updates any organization's information</p> <p><b>Organization Admin:</b> Requests changes to its organization's information</p> <ul> <li>short_name</li> <li>long_name</li> <li>authority</li> <li>aliases</li> <li>oversees</li> <li>root_or_tlr</li> <li>charter_or_scope</li> <li>product_list</li> <li>disclosure_policy</li> <li>contact_info.poc</li> <li>contact_info.poc_email</li> <li>contact_info.poc_phone</li> <li>contact_info.org_email</li> <li>cna_role_type</li> <li>cna_country</li> <li>vulnerability_advisory_locations</li> <li>advisory_location_require_credentials</li> <li>industry</li> <li>tl_root_start_date</li> <li>is_cna_discussion_list</li> </ul>",
"operationId": "orgUpdateSingle",
"parameters": [
{
Expand Down
12,096 changes: 1,795 additions & 10,301 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 4 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
{
"name": "cve-services",
"author": "Automation Working Group",
"version": "2.7.0",
"version": "2.7.1",
"license": "(CC0)",
"devDependencies": {
"@faker-js/faker": "^7.6.0",
"apidoc": "^0.53.1",
"chai": "^4.2.0",
"chai-arrays": "^2.0.0",
"chai-http": "^4.3.0",
Expand All @@ -21,7 +20,7 @@
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"mocha": "^10.1.0",
"mocha": "^10.8.2",
"nyc": "^15.1.0",
"sinon": "^15.0.4",
"standard": "^16.0.3"
Expand Down Expand Up @@ -64,13 +63,8 @@
"overrides": {
"mongo-cursor-pagination": {
"bson": "^6.10.1"
}
},
"apidoc": {
"name": "CVE-Services",
"version": "0.0.0",
"description": "Some Future Description",
"title": "CVE API Services"
},
"serialize-javascript": "^7.0.3"
},
"nyc": {
"exclude": "test-utils/"
Expand Down
9 changes: 9 additions & 0 deletions schemas/conversation/conversation.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@
"format": "date-time",
"description": "Timestamp when the message was posted"
},
"edited_at": {
"type": "string",
"format": "date-time",
"description": "Timestamp when the message was last edited"
},
"editor_id": {
"type": "string",
"description": "UUID of the user who last edited the message"
},
"last_updated": {
"type": "string",
"format": "date-time",
Expand Down
17 changes: 17 additions & 0 deletions schemas/conversation/update-conversation-response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://cve.mitre.org/schema/conversation/update-conversation-response.json",
"type": "object",
"title": "Update Conversation Response",
"description": "JSON Schema for the response when updating a conversation",
"properties": {
"message": {
"type": "string",
"description": "Response message"
},
"conversation": {
"$ref": "conversation.json",
"description": "The updated conversation message"
}
}
}
4 changes: 2 additions & 2 deletions src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ function getConstants () {
USER_ROLES: [
'ADMIN'
],
JOINT_APPROVAL_FIELDS: ['short_name', 'long_name', 'authority', 'aliases', 'oversees', 'root_or_tlr', 'charter_or', 'product_list', 'disclosure_policy', 'contact_info.poc', 'contact_info.poc_email', 'contact_info.poc_phone', 'contact_info.org_email', 'cna_role_type', 'cna_country', 'vulnerability_advisory_locations', 'advisory_location_require_credentials', 'industry', 'tl_root_start_date', 'is_cna_discussion_list'],
JOINT_APPROVAL_FIELDS_LEGACY: ['short_name', 'name', 'authority.active_roles'],
JOINT_APPROVAL_FIELDS: ['short_name', 'long_name', 'authority', 'aliases', 'oversees', 'root_or_tlr', 'charter_or_scope', 'product_list', 'disclosure_policy', 'contact_info.poc', 'contact_info.poc_email', 'contact_info.poc_phone', 'contact_info.org_email', 'cna_role_type', 'cna_country', 'vulnerability_advisory_locations', 'advisory_location_require_credentials', 'industry', 'tl_root_start_date', 'is_cna_discussion_list', 'hard_quota'],
JOINT_APPROVAL_FIELDS_LEGACY: ['short_name', 'name', 'authority.active_roles', 'policies.id_quota'],
USER_ROLE_ENUM: {
ADMIN: 'ADMIN'
},
Expand Down
77 changes: 76 additions & 1 deletion src/controller/org.controller/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ router.put('/registry/org/:shortname',
<li>aliases</li>
<li>oversees</li>
<li>root_or_tlr</li>
<li>charter_or</li>
<li>charter_or_scope</li>
<li>product_list</li>
<li>disclosure_policy</li>
<li>contact_info.poc</li>
Expand Down Expand Up @@ -1079,6 +1079,81 @@ router.post('/registry/org/:shortname/user/:username/revoke-role',
registryUserController.REVOKE_ROLE
)

router.put('/registry/org/:shortname/conversation/:index',
/*
#swagger.tags = ['Registry Organization']
#swagger.operationId = 'registryUserUpdateConversation'
#swagger.summary = "Update the conversation at the given index for the given organization (accessible to Secretariat or Org Admin)"
#swagger.description = "
<h2>Access Control</h2>
<p>User must belong to an organization with the <b>Secretariat</b> role or be an <b>Admin</b> of the organization</p>
<h2>Expected Behavior</h2>
<p><b>Admin User:</b> Allowed to update only the message body of any conversation posted by them</p>
<p><b>Secretariat:</b> Allowed to update the message body and/or visibility of any conversation</p>"
#swagger.parameters['shortname'] = { description: 'The shortname of the organization' }
#swagger.parameters['index'] = { description: 'The index of the conversation to update' }
#swagger.parameters['$ref'] = [
'#/components/parameters/apiEntityHeader',
'#/components/parameters/apiUserHeader',
'#/components/parameters/apiSecretHeader'
]
#swagger.responses[200] = {
description: 'Returns the updated conversation',
content: {
"application/json": {
schema: { $ref: '../schemas/conversation/update-conversation-response.json' }
}
}
}
#swagger.responses[400] = {
description: 'Bad Request',
content: {
"application/json": {
schema: { $ref: '../schemas/errors/bad-request.json' }
}
}
}
#swagger.responses[401] = {
description: 'Not Authenticated',
content: {
"application/json": {
schema: { $ref: '../schemas/errors/generic.json' }
}
}
}
#swagger.responses[403] = {
description: 'Forbidden',
content: {
"application/json": {
schema: { $ref: '../schemas/errors/generic.json' }
}
}
}
#swagger.responses[404] = {
description: 'Not Found',
content: {
"application/json": {
schema: { $ref: '../schemas/errors/generic.json' }
}
}
}
#swagger.responses[500] = {
description: 'Internal Server Error',
content: {
"application/json": {
schema: { $ref: '../schemas/errors/generic.json' }
}
}
}
*/
mw.useRegistry(),
mw.validateUser,
mw.onlyOrgWithPartnerRole,
parseError,
parsePostParams,
registryOrgController.EDIT_CONVERSATION
)

router.get('/org',
/*
#swagger.tags = ['Organization']
Expand Down
41 changes: 15 additions & 26 deletions src/controller/org.controller/org.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,36 +199,27 @@ async function getUser (req, res, next) {
*/
async function getOrgIdQuota (req, res, next) {
try {
const session = await mongoose.startSession()
const orgRepo = req.ctx.repositories.getBaseOrgRepository()
const requesterOrgShortName = req.ctx.org
const shortName = req.ctx.params.shortname

try {
session.startTransaction()
const requesterOrg = await orgRepo.findOneByShortName(requesterOrgShortName, { session })
const isSecretariat = await orgRepo.isSecretariat(requesterOrg, !req.useRegistry)

if (requesterOrgShortName !== shortName && !isSecretariat) {
logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization id quota can only be viewed by the users of the same organization or the Secretariat.' })
return res.status(403).json(error.notSameOrgOrSecretariat())
}
const requesterOrg = await orgRepo.findOneByShortName(requesterOrgShortName)
const isSecretariat = await orgRepo.isSecretariat(requesterOrg, !req.useRegistry)

const org = await orgRepo.getOrg(shortName, false, { session }, !req.useRegistry)
if (!org) { // a null org can only happen if the requestor is the Secretariat
logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization does not exist.' })
return res.status(404).json(error.orgDnePathParam(shortName))
}
if (requesterOrgShortName !== shortName && !isSecretariat) {
logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization id quota can only be viewed by the users of the same organization or the Secretariat.' })
return res.status(403).json(error.notSameOrgOrSecretariat())
}

const returnPayload = await orgRepo.getOrgIdQuota(org, !req.useRegistry)
logger.info({ uuid: req.ctx.uuid, message: 'The organization\'s id quota was returned to the user.', details: returnPayload })
return res.status(200).json(returnPayload)
} catch (error) {
await session.abortTransaction()
throw error
} finally {
await session.endSession()
const org = await orgRepo.getOrg(shortName, false, {}, !req.useRegistry)
if (!org) { // a null org can only happen if the requestor is the Secretariat
logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization does not exist.' })
return res.status(404).json(error.orgDnePathParam(shortName))
}

const returnPayload = await orgRepo.getOrgIdQuota(org, !req.useRegistry)
logger.info({ uuid: req.ctx.uuid, message: 'The organization\'s id quota was returned to the user.', details: returnPayload })
return res.status(200).json(returnPayload)
} catch (err) {
next(err)
}
Expand Down Expand Up @@ -301,8 +292,6 @@ async function createOrg (req, res, next) {
org: returnValue
}

// const userRepo = req.ctx.repositories.getUserRepository()
// payload.user_UUID = await userRepo.getUserUUID(req.ctx.user, payload.org_UUID)
logger.info(JSON.stringify(payload))
return res.status(200).json(responseMessage)
} catch (err) {
Expand Down Expand Up @@ -664,7 +653,7 @@ async function updateUser (req, res, next) {
*/
async function resetSecret (req, res, next) {
try {
const session = await mongoose.startSession()
const session = await mongoose.startSession({ causalConsistency: false })
const requesterOrgShortName = req.ctx.org
const requesterUsername = req.ctx.user
const targetOrgShortName = req.ctx.params.shortname
Expand Down
1 change: 0 additions & 1 deletion src/controller/org.controller/org.middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,6 @@ const QUERY_PARAMETERS = {
}

function parsePutParams (req, res, next) {
console.log('DEBUG: parsePutParams req.body:', JSON.stringify(req.body))
req.body = utils.deepRemoveEmpty(req.body)
utils.reqCtxMapping(req, 'body', [])
// Extract all possible query parameters
Expand Down
28 changes: 28 additions & 0 deletions src/controller/registry-org.controller/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,34 @@ class RegistryOrgControllerError extends idrErr.IDRError {
err.message = 'The requested user can not be created and added to the organization because the organization has hit its limit of 100 users. Contact the Secretariat.'
return err
}

conversationDne (shortname, index) {
const err = {}
err.error = 'CONVERSATION_DNE'
err.message = `The conversation at index ${index} does not exist for the ${shortname} organization.`
return err
}

notAllowedToEditConversation () {
const err = {}
err.error = 'NOT_ALLOWED_TO_EDIT_CONVERSATION'
err.message = 'You must be the original author or Secretariat to edit this conversation.'
return err
}

notAllowedToChangeConversationVisibility () {
const err = {}
err.error = 'NOT_ALLOWED_TO_CHANGE_CONVERSATION_VISIBILITY'
err.message = 'Only the Secretariat is allowed to change the visibility of a conversation.'
return err
}

invalidConversationObject () {
const err = {}
err.error = 'BAD_INPUT'
err.message = 'Parameters were invalid: conversation must be an object with a body.'
return err
}
}

module.exports = {
Expand Down
Loading
Loading