Skip to content

Commit

Permalink
Merge pull request #623 from CVEProject/dev
Browse files Browse the repository at this point in the history
#573,  #576, #605, #607 Update INT
  • Loading branch information
slubar authored Apr 8, 2022
2 parents a29f675 + 80f71cf commit cc746a7
Show file tree
Hide file tree
Showing 13 changed files with 230 additions and 48 deletions.
5 changes: 3 additions & 2 deletions src/controller/cve-id.controller/cve-id.middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ function parsePostParams (req, res, next) {

// Sanitizer for dates
function toDate (val) {
let value = val.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/)
let value = val.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(|Z|((-|\+|\s)\d{2}:\d{2}))$/)
let result
if (value) {
result = new Date(`${value[0]}.000+00:00`)
value[0] = value[0].replace(' ', '+') // Re-add literal '+' which was stripped
result = new Date(value[0])
} else {
value = val.match(/^\d{4}-\d{2}-\d{2}$/)
if (value) {
Expand Down
2 changes: 1 addition & 1 deletion src/controller/cve-id.controller/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ router.get('/cve-id/:id',
parseGetParams,
controller.CVEID_GET_SINGLE)
router.put('/cve-id/:id',
mw.onlyCnas,
mw.validateUser,
mw.onlyCnas,
param(['id']).isString().matches(/^CVE-[0-9]{4}-[0-9]{4,}$/, 'i'),
query(['state']).optional().isString().trim().escape().customSanitizer(val => { return val.toUpperCase() }).isIn(CHOICES),
query(['org']).optional().isString().trim().escape(),
Expand Down
29 changes: 15 additions & 14 deletions src/controller/cve.controller/cve.middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ function parseGetParams (req, res, next) {

// Sanitizer for dates
function toDate (val) {
let value = val.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/)
let value = val.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(|Z|((-|\+|\s)\d{2}:\d{2}))$/)
let result
if (value) {
result = new Date(`${value[0]}.000+00:00`)
value[0] = value[0].replace(' ', '+') // Re-add literal '+' which was stripped
result = new Date(value[0])
} else {
value = val.match(/^\d{4}-\d{2}-\d{2}$/)
if (value) {
Expand All @@ -52,19 +53,19 @@ function parseError (req, res, next) {
next()
}

function onlyOneEnglishDescription (arr) {
const arrayLength = arr.length
let numEnglishFound = 0 // for checking how many times an english field shows up
for (var i = 0; i < arrayLength; i++) {
if (arr[i].lang === 'en') {
numEnglishFound += 1
}

if (numEnglishFound > 1) { // return error if more than 1 english description is found
return null
function uniqueEnglishDescription (rejectedReasonsArr) {
const langArray = rejectedReasonsArr.map(function (reason) { return reason.lang.toLowerCase() })// create arr of lowercase lang values
const foundValues = new Set() // set to hold languages found
// loop through the lang array and find duplicates
for (var i = 0; i < langArray.length; i++) {
if (langArray[i].startsWith('en')) { // check case only if lang starts with "en"
if (foundValues.has(langArray[i])) {
return false // duplicate found so return false
}
foundValues.add(langArray[i]) // add each unique value to set
}
}
return numEnglishFound
return true // if no duplicate found, then all lang values were unique
}

function validateRejectBody (req, res, next) {
Expand Down Expand Up @@ -108,6 +109,6 @@ module.exports = {
parseError,
toDate,
validateCveCnaContainerJsonSchema,
onlyOneEnglishDescription,
uniqueEnglishDescription,
validateRejectBody
}
14 changes: 7 additions & 7 deletions src/controller/cve.controller/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const router = express.Router()
const mw = require('../../middleware/middleware')
const controller = require('./cve.controller')
const { body, param, query } = require('express-validator')
const { parseGetParams, parsePostParams, parseError, toDate, validateCveCnaContainerJsonSchema, validateRejectBody, onlyOneEnglishDescription } = require('./cve.middleware')
const { parseGetParams, parsePostParams, parseError, toDate, validateCveCnaContainerJsonSchema, validateRejectBody, uniqueEnglishDescription } = require('./cve.middleware')
const CONSTANTS = require('../../constants')
const CHOICES = [CONSTANTS.CVE_STATES.REJECTED, CONSTANTS.CVE_STATES.PUBLISHED]

Expand Down Expand Up @@ -56,8 +56,8 @@ router.put('/cve/:id',
controller.CVE_UPDATE_SINGLE)

router.post('/cve/:id/cna',
mw.onlyCnas,
mw.validateUser,
mw.onlyCnas,
validateCveCnaContainerJsonSchema,
param(['id']).isString().matches(/^CVE-[0-9]{4}-[0-9]{4,}$/i),
parseError,
Expand All @@ -66,8 +66,8 @@ router.post('/cve/:id/cna',
controller.CVE_SUBMIT_CNA)

router.put('/cve/:id/cna',
mw.onlyCnas,
mw.validateUser,
mw.onlyCnas,
validateCveCnaContainerJsonSchema,
param(['id']).isString().matches(/^CVE-[0-9]{4}-[0-9]{4,}$/i),
parseError,
Expand All @@ -76,12 +76,12 @@ router.put('/cve/:id/cna',
controller.CVE_UPDATE_CNA)

router.post('/cve/:id/reject',
mw.onlyCnas,
mw.validateUser,
mw.onlyCnas,
validateRejectBody,
param(['id']).isString().matches(/^CVE-[0-9]{4}-[0-9]{4,}$/i),
body(['cnaContainer.rejectedReasons']).isArray().custom((arr) => {
if (onlyOneEnglishDescription(arr) !== 1) {
if (!uniqueEnglishDescription(arr)) {
throw new Error(400, 'Bad request, more than one English description found')
}
return true
Expand All @@ -93,12 +93,12 @@ router.post('/cve/:id/reject',
controller.CVE_REJECT_RECORD)

router.put('/cve/:id/reject',
mw.onlyCnas,
mw.validateUser,
mw.onlyCnas,
validateRejectBody,
param(['id']).isString().matches(/^CVE-[0-9]{4}-[0-9]{4,}$/i),
body(['cnaContainer.rejectedReasons']).isArray().custom((arr) => {
if (onlyOneEnglishDescription(arr) !== 1) {
if (!uniqueEnglishDescription(arr)) {
throw new Error(400, 'Bad request, more than one English description found')
}
return true
Expand Down
16 changes: 15 additions & 1 deletion src/middleware/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ class MiddlewareError extends idrErr.IDRError {
return err
}

cnaDoesNotExist (shortname) { // mw
const err = {}
err.error = 'CNA_DOES_NOT_EXIST'
err.message = `The '${shortname}' organization designated by the shortname parameter does not exist.`
return err
}

orgDoesNotOwnId (org, id) {
const err = {
error: 'ORG_DOES_NOT_OWN_ID',
Expand All @@ -54,7 +61,14 @@ class MiddlewareError extends idrErr.IDRError {
return err
}

recordTooLarge () {
genericBadRequest (errors) { // mw
const err = {}
err.error = 'BAD_REQUEST'
err.message = errors
return err
}

recordTooLarge () { // mw
const err = {}
err.error = 'RECORD_TOO_LARGE'
err.message = 'Records must be less than 16MB.'
Expand Down
17 changes: 12 additions & 5 deletions src/middleware/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,10 @@ async function onlyCnas (req, res, next) {

try {
const org = await orgRepo.findOneByShortName(shortName) // org exists
if (org.authority.active_roles.includes(CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT)) {
if (org === null) {
logger.info({ uuid: req.ctx.uuid, message: shortName + ' is NOT a ' + CONSTANTS.AUTH_ROLE_ENUM.CNA })
return res.status(404).json(error.cnaDoesNotExist(shortName))
} else if (org.authority.active_roles.includes(CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT)) {
logger.info({ uuid: req.ctx.uuid, message: org.short_name + ' is a ' + CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT + ' so until Root organizations are implemented this role is allowed.' })
next()
} else if (org.authority.active_roles.includes(CONSTANTS.AUTH_ROLE_ENUM.CNA)) { // the org is a CNA
Expand Down Expand Up @@ -207,14 +210,18 @@ function validateCveJsonSchema (req, res, next) {

function validateJsonSyntax (err, req, res, next) {
if (err.status && err.message) {
if (err.message.includes('Unexpected token')) {
if (err.message.includes('request entity too large')) {
console.warn('Request failed validation because entity too large')
console.info((JSON.stringify(err)))
return res.status(413).json(error.recordTooLarge(errors))
} else if (err.status === 400) {
console.warn('Request failed validation because JSON syntax is incorrect')
console.info((JSON.stringify(err)))
return res.status(400).json(error.invalidJsonSyntax(err.message))
} else if (err.message.includes('request entity too large')) {
console.warn('Request failed validation because entity too large')
} else {
console.warn('Request failed')
console.info((JSON.stringify(err)))
return res.status(413).json(error.recordTooLarge(errors))
return res.status(400).json(error.genericBadRequest(err.message))
}
} else {
next(err)
Expand Down
15 changes: 14 additions & 1 deletion test-http/src/test/cve_id_tests/cve_id_as_org_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,10 +317,12 @@ def test_get_cve_id_by_time_modified(org_admin_headers):
n_ids = 10
time.sleep(1)
t_before = dt.datetime.now().strftime('%Y-%m-%dT%H:%M:%S')
t_before_alt = dt.datetime.now().strftime('%Y-%m-%dT%H:%M:%S+00:00')
time.sleep(1)
res_ids = get_reserve_cve_ids(n_ids, utils.CURRENT_YEAR, org_admin_headers['CVE-API-ORG'])
time.sleep(1)
t_after = dt.datetime.now().strftime('%Y-%m-%dT%H:%M:%S')
t_after_alt = dt.datetime.now().strftime('%Y-%m-%dT%H:%M:%S+00:00')

res_get_ids = requests.get(
f'{env.AWG_BASE_URL}{CVE_ID_URL}',
Expand All @@ -330,8 +332,19 @@ def test_get_cve_id_by_time_modified(org_admin_headers):
'time_modified.gt': t_before
}
)
# Test alternate timezone format
res_get_ids_alt = requests.get(
f'{env.AWG_BASE_URL}{CVE_ID_URL}',
headers=utils.BASE_HEADERS,
params={
'time_modified.lt': t_after_alt,
'time_modified.gt': t_before_alt
}
)
ok_response_contains(res_get_ids, f'CVE-{utils.CURRENT_YEAR}-')
assert len(json.loads(res_get_ids.content.decode())['cve_ids']) == n_ids
ok_response_contains(res_get_ids_alt, f'CVE-{utils.CURRENT_YEAR}-')
assert len(json.loads(res_get_ids_alt.content.decode())['cve_ids']) == n_ids


def test_get_cve_id_with_params(org_admin_headers):
Expand Down Expand Up @@ -470,4 +483,4 @@ def get_reserve_cve_ids(
'cve_year': f'{year}',
'short_name': cna_short_name
}
)
)
65 changes: 61 additions & 4 deletions test-http/src/test/cve_tests/cve.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ def test_get_cve_by_time_modified():
time.sleep(1)
post_cve('CVE-2021-0005_published', 'CVE-2021-0005')
t_before = dt.datetime.now().strftime('%Y-%m-%dT%H:%M:%S')
t_before_alt = dt.datetime.now().strftime('%Y-%m-%dT%H:%M:%S+00:00')
time.sleep(1)
update_cve('CVE-2021-0004_published', 'CVE-2021-0004')
time.sleep(1)
update_cve('CVE-2021-0005_published', 'CVE-2021-0005')
time.sleep(1)
t_after = dt.datetime.now().strftime('%Y-%m-%dT%H:%M:%S')
t_after_alt = dt.datetime.now().strftime('%Y-%m-%dT%H:%M:%S+00:00')

res = requests.get(
f'{env.AWG_BASE_URL}{CVE_URL}/',
Expand All @@ -51,8 +53,19 @@ def test_get_cve_by_time_modified():
'time_modified.gt': t_before
}
)
# Alternat timezone format
res_alt = requests.get(
f'{env.AWG_BASE_URL}{CVE_URL}/',
headers=utils.BASE_HEADERS,
params={
'time_modified.lt': t_after_alt,
'time_modified.gt': t_before_alt
}
)
assert res.status_code == utils.HTTP_OK
assert len(json.loads(res.content.decode())['cveRecords']) >= 2
assert res_alt.status_code == utils.HTTP_OK
assert len(json.loads(res_alt.content.decode())['cveRecords']) >= 2


def test_get_cve_by_count_only_true():
Expand Down Expand Up @@ -350,8 +363,8 @@ def test_submit_record_rejection_id_dne():
assert res.status_code == 403


def test_submit_record_rejection_multiple_english_descriptions():
""" submit a reject request with descriptions array that has more than one english description """
def test_submit_record_rejection_multiple_different_english_values():
""" submit a reject request with descriptions array that has multiple different English values (ex: "en" and "en-Ca") """
res = requests.post(
f'{env.AWG_BASE_URL}/api/cve-id/',
headers=utils.BASE_HEADERS,
Expand All @@ -362,14 +375,58 @@ def test_submit_record_rejection_multiple_english_descriptions():
}
)
id_num = json.loads(res.content.decode())['cve_ids'][0]['cve_id'] # obtain id number
with open('./src/test/cve_tests/cve_record_fixtures/rejectBodyMultipleEngDescriptions.json') as json_file:
with open('./src/test/cve_tests/cve_record_fixtures/rejectBodyMultipleDiffEngValues.json') as json_file:
data = json.load(json_file)
res = requests.post(
f'{env.AWG_BASE_URL}{CVE_URL}/{id_num}/reject',
headers=utils.BASE_HEADERS,
json=data
)
assert res.status_code == 400
assert res.status_code == 200 # lang values are unique


def test_submit_record_rejection_multiple_non_English_values():
""" submit a reject request with descriptions array that has multiple non English values (ex: "fr" and "fr") """
res = requests.post(
f'{env.AWG_BASE_URL}/api/cve-id/',
headers=utils.BASE_HEADERS,
params={
'amount': 1,
'cve_year': 2000,
'short_name': 'mitre'
}
)
id_num = json.loads(res.content.decode())['cve_ids'][0]['cve_id'] # obtain id number
with open('./src/test/cve_tests/cve_record_fixtures/rejectBodyMultipleNonEngValues.json') as json_file:
data = json.load(json_file)
res = requests.post(
f'{env.AWG_BASE_URL}{CVE_URL}/{id_num}/reject',
headers=utils.BASE_HEADERS,
json=data
)
assert res.status_code == 200 # lang values are unique


def test_submit_record_rejection_multiple_same_English_values():
""" submit a reject request with descriptions array that has multiple same English values (ex: "en-Gb" and "en-Gb") """
res = requests.post(
f'{env.AWG_BASE_URL}/api/cve-id/',
headers=utils.BASE_HEADERS,
params={
'amount': 1,
'cve_year': 2000,
'short_name': 'mitre'
}
)
id_num = json.loads(res.content.decode())['cve_ids'][0]['cve_id'] # obtain id number
with open('./src/test/cve_tests/cve_record_fixtures/rejectBodyMultipleSameEngValues.json') as json_file:
data = json.load(json_file)
res = requests.post(
f'{env.AWG_BASE_URL}{CVE_URL}/{id_num}/reject',
headers=utils.BASE_HEADERS,
json=data
)
assert res.status_code == 400 # lang values are not unique


#### PUT /cve/:id ####
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,7 @@
]
},
{
"lang": "en",
"value": "I professional site herself recently behavior. Situation institution meeting recognize successful.",
"supportingMedia": [
{
"type": "test/markdown",
"base64": false,
"value": "*this* _is_ supporting media in ~markdown~"
}
]
},
{
"lang": "en",
"lang": "en-Ca",
"value": "I professional site herself recently behavior. Situation institution meeting recognize successful.",
"supportingMedia": [
{
Expand Down
Loading

0 comments on commit cc746a7

Please sign in to comment.