From 2344cf4c4c025499bb7978e5a9f9e2a323b69ca2 Mon Sep 17 00:00:00 2001 From: Sun Date: Fri, 29 Sep 2023 13:35:07 -0400 Subject: [PATCH 01/26] updating software service and new software repository --- app/repository/software-repository.js | 13 + app/services/software-service.js | 382 +------------------------- 2 files changed, 20 insertions(+), 375 deletions(-) create mode 100644 app/repository/software-repository.js diff --git a/app/repository/software-repository.js b/app/repository/software-repository.js new file mode 100644 index 00000000..4c7fccad --- /dev/null +++ b/app/repository/software-repository.js @@ -0,0 +1,13 @@ +'use strict'; + +const BaseRepository = require('./_base.repository'); +const Software = require('../models/software-model'); + +class SoftwareRepository extends BaseRepository { + + constructor() { + super(Software); + } +} + +module.exports = new SoftwareRepository(); \ No newline at end of file diff --git a/app/services/software-service.js b/app/services/software-service.js index a9711124..9d6a056f 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -20,381 +20,13 @@ const errors = { }; exports.errors = errors; -exports.retrieveAll = function(options, callback) { - // Build the query - const query = {}; - if (!options.includeRevoked) { - query['stix.revoked'] = { $in: [null, false] }; - } - if (!options.includeDeprecated) { - query['stix.x_mitre_deprecated'] = { $in: [null, false] }; - } - if (typeof options.state !== 'undefined') { - if (Array.isArray(options.state)) { - query['workspace.workflow.state'] = { $in: options.state }; - } - else { - query['workspace.workflow.state'] = options.state; - } - } - if (typeof options.domain !== 'undefined') { - if (Array.isArray(options.domain)) { - query['stix.x_mitre_domains'] = { $in: options.domain }; - } - else { - query['stix.x_mitre_domains'] = options.domain; - } - } - if (typeof options.platform !== 'undefined') { - if (Array.isArray(options.platform)) { - query['stix.x_mitre_platforms'] = { $in: options.platform }; - } - else { - query['stix.x_mitre_platforms'] = options.platform; - } - } - if (typeof options.lastUpdatedBy !== 'undefined') { - query['workspace.workflow.created_by_user_account'] = lastUpdatedByQueryHelper(options.lastUpdatedBy); - } - - // Build the aggregation - // - Group the documents by stix.id, sorted by stix.modified - // - Use the first document in each group (according to the value of stix.modified) - // - Then apply query, skip and limit options - const aggregation = [ - { $sort: { 'stix.id': 1, 'stix.modified': -1 } }, - { $group: { _id: '$stix.id', document: { $first: '$$ROOT' }}}, - { $replaceRoot: { newRoot: '$document' }}, - { $sort: { 'stix.id': 1 }}, - { $match: query } - ]; - - if (typeof options.search !== 'undefined') { - options.search = regexValidator.sanitizeRegex(options.search); - const match = { $match: { $or: [ - { 'stix.name': { '$regex': options.search, '$options': 'i' }}, - { 'stix.description': { '$regex': options.search, '$options': 'i' }}, - { 'workspace.attack_id': { '$regex': options.search, '$options': 'i' }} - ]}}; - aggregation.push(match); - } - - const facet = { - $facet: { - totalCount: [ { $count: 'totalCount' }], - documents: [ ] - } - }; - if (options.offset) { - facet.$facet.documents.push({ $skip: options.offset }); - } - else { - facet.$facet.documents.push({ $skip: 0 }); - } - if (options.limit) { - facet.$facet.documents.push({ $limit: options.limit }); - } - aggregation.push(facet); - - // Retrieve the documents - Software.aggregate(aggregation, function(err, results) { - if (err) { - return callback(err); - } - else { - identitiesService.addCreatedByAndModifiedByIdentitiesToAll(results[0].documents) - .then(function() { - if (options.includePagination) { - let derivedTotalCount = 0; - if (results[0].totalCount.length > 0) { - derivedTotalCount = results[0].totalCount[0].totalCount; - } - const returnValue = { - pagination: { - total: derivedTotalCount, - offset: options.offset, - limit: options.limit - }, - data: results[0].documents - }; - return callback(null, returnValue); - } - else { - return callback(null, results[0].documents); - } - }); - } - }); -}; - -exports.retrieveById = function(stixId, options, callback) { - // versions=all Retrieve all software with the stixId - // versions=latest Retrieve the software with the latest modified date for this stixId - - if (!stixId) { - const error = new Error(errors.missingParameter); - error.parameterName = 'stixId'; - return callback(error); - } - - if (options.versions === 'all') { - Software.find({'stix.id': stixId}) - .lean() - .exec(function (err, software) { - if (err) { - if (err.name === 'CastError') { - const error = new Error(errors.badlyFormattedParameter); - error.parameterName = 'stixId'; - return callback(error); - } else { - return callback(err); - } - } else { - identitiesService.addCreatedByAndModifiedByIdentitiesToAll(software) - .then(() => callback(null, software)); - } - }); - } - else if (options.versions === 'latest') { - Software.findOne({ 'stix.id': stixId }) - .sort('-stix.modified') - .lean() - .exec(function(err, software) { - if (err) { - if (err.name === 'CastError') { - const error = new Error(errors.badlyFormattedParameter); - error.parameterName = 'stixId'; - return callback(error); - } - else { - return callback(err); - } - } - else { - // Note: document is null if not found - if (software) { - identitiesService.addCreatedByAndModifiedByIdentities(software) - .then(() => callback(null, [ software ])); - } - else { - return callback(null, []); - } - } - }); - } - else { - const error = new Error(errors.invalidQueryStringParameter); - error.parameterName = 'versions'; - return callback(error); - } -}; - -exports.retrieveVersionById = function(stixId, modified, callback) { - // Retrieve the versions of the software with the matching stixId and modified date - - if (!stixId) { - const error = new Error(errors.missingParameter); - error.parameterName = 'stixId'; - return callback(error); - } - - if (!modified) { - const error = new Error(errors.missingParameter); - error.parameterName = 'modified'; - return callback(error); - } - - Software.findOne({ 'stix.id': stixId, 'stix.modified': modified }, function(err, software) { - if (err) { - if (err.name === 'CastError') { - const error = new Error(errors.badlyFormattedParameter); - error.parameterName = 'stixId'; - return callback(error); - } - else { - return callback(err); - } - } - else { - // Note: document is null if not found - if (software) { - identitiesService.addCreatedByAndModifiedByIdentities(software) - .then(() => callback(null, software)); - } - else { - return callback(); - } - } - }); -}; +const BaseService = require('./_base.service'); +const SoftwareRepository = require('../repository/software-repository'); -exports.createIsAsync = true; -exports.create = async function(data, options) { - // This function handles two use cases: - // 1. This is a completely new object. Create a new object and generate the stix.id if not already - // provided. Set both stix.created_by_ref and stix.x_mitre_modified_by_ref to the organization identity. - // 2. This is a new version of an existing object. Create a new object with the specified id. - // Set stix.x_mitre_modified_by_ref to the organization identity. - - // is_family defaults to true for malware, not allowed for tools - if (data.stix && data.stix.type === 'malware' && typeof data.stix.is_family !== 'boolean') { - data.stix.is_family = true; - } - else if (data.stix && data.stix.type === 'tool' && data.stix.is_family !== undefined) { - const err = new Error(errors.propertyNotAllowed); - err.propertyName = 'stix.is_family'; - throw err; - } - - // Create the document - const software = new Software(data); - - options = options || {}; - if (!options.import) { - // Set the ATT&CK Spec Version - software.stix.x_mitre_attack_spec_version = software.stix.x_mitre_attack_spec_version ?? config.app.attackSpecVersion; - - // Record the user account that created the object - if (options.userAccountId) { - software.workspace.workflow.created_by_user_account = options.userAccountId; - } - - // Set the default marking definitions - await attackObjectsService.setDefaultMarkingDefinitions(software); - - // Get the organization identity - const organizationIdentityRef = await systemConfigurationService.retrieveOrganizationIdentityRef(); - - // Check for an existing object - let existingObject; - if (software.stix.id) { - existingObject = await Software.findOne({ 'stix.id': software.stix.id }); - } - - if (existingObject) { - // New version of an existing object - // Only set the x_mitre_modified_by_ref property - software.stix.x_mitre_modified_by_ref = organizationIdentityRef; - } - else { - // New object - // Assign a new STIX id if not already provided - if (software.stix.type === 'tool') { - software.stix.id = software.stix.id || `tool--${uuid.v4()}`; - } - else { - software.stix.id = software.stix.id || `malware--${uuid.v4()}`; - } - - // Set the created_by_ref and x_mitre_modified_by_ref properties - software.stix.created_by_ref = organizationIdentityRef; - software.stix.x_mitre_modified_by_ref = organizationIdentityRef; - } - } - - // Save the document in the database - try { - const savedSoftware = await software.save(); - return savedSoftware; - } - catch(err) { - if (err.name === 'MongoServerError' && err.code === 11000) { - // 11000 = Duplicate index - const error = new Error(errors.duplicateId); - throw error; - } - else { - throw err; - } - } -}; - -exports.updateFull = function(stixId, stixModified, data, callback) { - if (!stixId) { - const error = new Error(errors.missingParameter); - error.parameterName = 'stixId'; - return callback(error); - } - - if (!stixModified) { - const error = new Error(errors.missingParameter); - error.parameterName = 'modified'; - return callback(error); - } - - Software.findOne({ 'stix.id': stixId, 'stix.modified': stixModified }, function(err, document) { - if (err) { - if (err.name === 'CastError') { - var error = new Error(errors.badlyFormattedParameter); - error.parameterName = 'stixId'; - return callback(error); - } - else { - return callback(err); - } - } - else if (!document) { - // document not found - return callback(null); - } - else { - // Copy data to found document and save - Object.assign(document, data); - document.save(function(err, savedDocument) { - if (err) { - if (err.name === 'MongoServerError' && err.code === 11000) { - // 11000 = Duplicate index - var error = new Error(errors.duplicateId); - return callback(error); - } - else { - return callback(err); - } - } - else { - return callback(null, savedDocument); - } - }); - } - }); -}; - -exports.deleteVersionById = function (stixId, stixModified, callback) { - if (!stixId) { - const error = new Error(errors.missingParameter); - error.parameterName = 'stixId'; - return callback(error); +class SoftwareService extends BaseService { + constructor () { + super(SoftwareRepository, Software); + this.retrieveAllTechniques = null; } - if (!stixModified) { - const error = new Error(errors.missingParameter); - error.parameterName = 'modified'; - return callback(error); - } - - Software.findOneAndRemove({ 'stix.id': stixId, 'stix.modified': stixModified }, function (err, software) { - if (err) { - return callback(err); - } else { - //Note: software is null if not found - return callback(null, software); - } - }); -}; - -exports.deleteById = function (stixId, callback) { - if (!stixId) { - const error = new Error(errors.missingParameter); - error.parameterName = 'stixId'; - return callback(error); - } - - Software.deleteMany({ 'stix.id': stixId }, function (err, software) { - if (err) { - return callback(err); - } else { - //Note: software is null if not found - return callback(null, software); - } - }); -}; +} From 818dc641025d27abf6d296c19f057fe33b854c98 Mon Sep 17 00:00:00 2001 From: Sun Date: Fri, 29 Sep 2023 13:45:53 -0400 Subject: [PATCH 02/26] changing tests --- app/tests/api/software/software.spec.js | 498 +++++++++--------------- 1 file changed, 176 insertions(+), 322 deletions(-) diff --git a/app/tests/api/software/software.spec.js b/app/tests/api/software/software.spec.js index 908ba20d..da92ed53 100644 --- a/app/tests/api/software/software.spec.js +++ b/app/tests/api/software/software.spec.js @@ -78,245 +78,172 @@ describe('Software API', function () { passportCookie = await login.loginAnonymous(app); }); - it('GET /api/software returns an empty array of software', function (done) { - request(app) + it('GET /api/software returns an empty array of software', async function () { + const res = await request(app) .get('/api/software') .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - done(err); - } - else { - // We expect to get an empty array - const software = res.body; - expect(software).toBeDefined(); - expect(Array.isArray(software)).toBe(true); - expect(software.length).toBe(0); - done(); - } - }); + .expect('Content-Type', /json/); + + // We expect to get an empty array + const software = res.body; + expect(software).toBeDefined(); + expect(Array.isArray(software)).toBe(true); + expect(software.length).toBe(0); }); - it('POST /api/software does not create an empty software', function (done) { + it('POST /api/software does not create an empty software', async function () { const body = { }; - request(app) + const res = await request(app) .post('/api/software') .send(body) .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) - .expect(400) - .end(function(err, res) { - if (err) { - done(err); - } - else { - done(); - } - }); + .expect(400); }); - it('POST /api/software does not create a software missing the name property', function (done) { + it('POST /api/software does not create a software missing the name property', async function () { const timestamp = new Date().toISOString(); invalidMissingName.stix.created = timestamp; invalidMissingName.stix.modified = timestamp; const body = invalidMissingName; - request(app) + const res = await request(app) .post('/api/software') .send(body) .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) - .expect(400) - .end(function(err, res) { - if (err) { - done(err); - } - else { - done(); - } - }); + .expect(400); }); - it('POST /api/software does not create a software (tool) with the is_family property', function (done) { + it('POST /api/software does not create a software (tool) with the is_family property', async function () { const timestamp = new Date().toISOString(); invalidToolIncludesIsFamily.stix.created = timestamp; invalidToolIncludesIsFamily.stix.modified = timestamp; const body = invalidToolIncludesIsFamily; - request(app) + const res = await request(app) .post('/api/software') .send(body) .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) - .expect(400) - .end(function(err, res) { - if (err) { - done(err); - } - else { - done(); - } - }); + .expect(400); }); let software1; - it('POST /api/software creates a software', function (done) { + it('POST /api/software creates a software', async function () { const timestamp = new Date().toISOString(); initialObjectData.stix.created = timestamp; initialObjectData.stix.modified = timestamp; const body = initialObjectData; - request(app) + const res = await request(app) .post('/api/software') .send(body) .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(201) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - done(err); - } - else { - // We expect to get the created software - software1 = res.body; - expect(software1).toBeDefined(); - expect(software1.stix).toBeDefined(); - expect(software1.stix.id).toBeDefined(); - expect(software1.stix.created).toBeDefined(); - expect(software1.stix.modified).toBeDefined(); - expect(software1.stix.x_mitre_attack_spec_version).toBe(config.app.attackSpecVersion); - - done(); - } - }); + .expect('Content-Type', /json/); + + // We expect to get the created software + software1 = res.body; + expect(software1).toBeDefined(); + expect(software1.stix).toBeDefined(); + expect(software1.stix.id).toBeDefined(); + expect(software1.stix.created).toBeDefined(); + expect(software1.stix.modified).toBeDefined(); + expect(software1.stix.x_mitre_attack_spec_version).toBe(config.app.attackSpecVersion); + }); - it('GET /api/software returns the added software', function (done) { - request(app) + it('GET /api/software returns the added software', async function () { + const res = await request(app) .get('/api/software') .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - done(err); - } - else { - // We expect to get one software in an array - const software = res.body; - expect(software).toBeDefined(); - expect(Array.isArray(software)).toBe(true); - expect(software.length).toBe(1); - done(); - } - }); + .expect('Content-Type', /json/); + + // We expect to get one software in an array + const software = res.body; + expect(software).toBeDefined(); + expect(Array.isArray(software)).toBe(true); + expect(software.length).toBe(1); }); - it('GET /api/software/:id should not return a software when the id cannot be found', function (done) { - request(app) + it('GET /api/software/:id should not return a software when the id cannot be found', async function () { + const res = await request(app) .get('/api/software/not-an-id') .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) - .expect(404) - .end(function (err, res) { - if (err) { - done(err); - } else { - done(); - } - }); + .expect(404); }); - it('GET /api/software/:id returns the added software', function (done) { - request(app) + it('GET /api/software/:id returns the added software', async function () { + const res = await request(app) .get('/api/software/' + software1.stix.id) .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - done(err); - } - else { - // We expect to get one software in an array - const softwareObjects = res.body; - expect(softwareObjects).toBeDefined(); - expect(Array.isArray(softwareObjects)).toBe(true); - expect(softwareObjects.length).toBe(1); - - const software= softwareObjects[0]; - expect(software).toBeDefined(); - expect(software.stix).toBeDefined(); - expect(software.stix.id).toBe(software1.stix.id); - expect(software.stix.type).toBe(software1.stix.type); - expect(software.stix.name).toBe(software1.stix.name); - expect(software.stix.description).toBe(software1.stix.description); - expect(software.stix.is_family).toBe(software1.stix.is_family); - expect(software.stix.spec_version).toBe(software1.stix.spec_version); - expect(software.stix.object_marking_refs).toEqual(expect.arrayContaining(software1.stix.object_marking_refs)); - expect(software.stix.created_by_ref).toBe(software1.stix.created_by_ref); - expect(software.stix.x_mitre_version).toBe(software1.stix.x_mitre_version); - expect(software.stix.x_mitre_aliases).toEqual(expect.arrayContaining(software1.stix.x_mitre_aliases)); - expect(software.stix.x_mitre_platforms).toEqual(expect.arrayContaining(software1.stix.x_mitre_platforms)); - expect(software.stix.x_mitre_contributors).toEqual(expect.arrayContaining(software1.stix.x_mitre_contributors)); - expect(software.stix.x_mitre_attack_spec_version).toBe(software1.stix.x_mitre_attack_spec_version); - - done(); - } - }); + .expect('Content-Type', /json/); + + // We expect to get one software in an array + const softwareObjects = res.body; + expect(softwareObjects).toBeDefined(); + expect(Array.isArray(softwareObjects)).toBe(true); + expect(softwareObjects.length).toBe(1); + + const software= softwareObjects[0]; + expect(software).toBeDefined(); + expect(software.stix).toBeDefined(); + expect(software.stix.id).toBe(software1.stix.id); + expect(software.stix.type).toBe(software1.stix.type); + expect(software.stix.name).toBe(software1.stix.name); + expect(software.stix.description).toBe(software1.stix.description); + expect(software.stix.is_family).toBe(software1.stix.is_family); + expect(software.stix.spec_version).toBe(software1.stix.spec_version); + expect(software.stix.object_marking_refs).toEqual(expect.arrayContaining(software1.stix.object_marking_refs)); + expect(software.stix.created_by_ref).toBe(software1.stix.created_by_ref); + expect(software.stix.x_mitre_version).toBe(software1.stix.x_mitre_version); + expect(software.stix.x_mitre_aliases).toEqual(expect.arrayContaining(software1.stix.x_mitre_aliases)); + expect(software.stix.x_mitre_platforms).toEqual(expect.arrayContaining(software1.stix.x_mitre_platforms)); + expect(software.stix.x_mitre_contributors).toEqual(expect.arrayContaining(software1.stix.x_mitre_contributors)); + expect(software.stix.x_mitre_attack_spec_version).toBe(software1.stix.x_mitre_attack_spec_version); + }); - it('PUT /api/software updates a software', function (done) { + it('PUT /api/software updates a software', async function () { const originalModified = software1.stix.modified; const timestamp = new Date().toISOString(); software1.stix.modified = timestamp; software1.stix.description = 'This is an updated software.' const body = software1; - request(app) + const res = await request(app) .put('/api/software/' + software1.stix.id + '/modified/' + originalModified) .send(body) .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - done(err); - } - else { - // We expect to get the updated software - const software = res.body; - expect(software).toBeDefined(); - expect(software.stix.id).toBe(software1.stix.id); - expect(software.stix.modified).toBe(software1.stix.modified); - done(); - } - }); + .expect('Content-Type', /json/); + + // We expect to get the updated software + const software = res.body; + expect(software).toBeDefined(); + expect(software.stix.id).toBe(software1.stix.id); + expect(software.stix.modified).toBe(software1.stix.modified); + }); - it('POST /api/software does not create a software with the same id and modified date', function (done) { + it('POST /api/software does not create a software with the same id and modified date', async function () { const body = software1; - request(app) + const res = await request(app) .post('/api/software') .send(body) .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) - .expect(409) - .end(function(err, res) { - if (err) { - done(err); - } - else { - done(); - } - }); + .expect(409); }); let software2; - it('POST /api/software should create a new version of a software with a duplicate stix.id but different stix.modified date', function (done) { + it('POST /api/software should create a new version of a software with a duplicate stix.id but different stix.modified date', async function () { software2 = _.cloneDeep(software1); software2._id = undefined; software2.__t = undefined; @@ -324,121 +251,90 @@ describe('Software API', function () { const timestamp = new Date().toISOString(); software2.stix.modified = timestamp; const body = software2; - request(app) + const res = await request(app) .post('/api/software') .send(body) .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(201) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - done(err); - } - else { - // We expect to get the created software - const software = res.body; - expect(software).toBeDefined(); - done(); - } - }); + .expect('Content-Type', /json/); + + // We expect to get the created software + const software = res.body; + expect(software).toBeDefined(); + }); - it('GET /api/software returns the latest added software', function (done) { - request(app) + it('GET /api/software returns the latest added software', async function () { + const res = await request(app) .get('/api/software/' + software2.stix.id) .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - done(err); - } - else { - // We expect to get one software in an array - const software = res.body; - expect(software).toBeDefined(); - expect(Array.isArray(software)).toBe(true); - expect(software.length).toBe(1); - const softwre = software[0]; - expect(softwre.stix.id).toBe(software2.stix.id); - expect(softwre.stix.modified).toBe(software2.stix.modified); - done(); - } - }); + .expect('Content-Type', /json/); + + // We expect to get one software in an array + const software = res.body; + expect(software).toBeDefined(); + expect(Array.isArray(software)).toBe(true); + expect(software.length).toBe(1); + const softwre = software[0]; + expect(softwre.stix.id).toBe(software2.stix.id); + expect(softwre.stix.modified).toBe(software2.stix.modified); }); - it('GET /api/software returns all added software', function (done) { - request(app) + it('GET /api/software returns all added software', async function () { + const res = await request(app) .get('/api/software/' + software1.stix.id + '?versions=all') .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - done(err); - } - else { - // We expect to get two software in an array - const software = res.body; - expect(software).toBeDefined(); - expect(Array.isArray(software)).toBe(true); - expect(software.length).toBe(2); - done(); - } - }); + .expect('Content-Type', /json/); + + // We expect to get two software in an array + const software = res.body; + expect(software).toBeDefined(); + expect(Array.isArray(software)).toBe(true); + expect(software.length).toBe(2); + }); - it('GET /api/software/:id/modified/:modified returns the first added software', function (done) { - request(app) + it('GET /api/software/:id/modified/:modified returns the first added software', async function () { + const res = await request(app) .get('/api/software/' + software1.stix.id + '/modified/' + software1.stix.modified) .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - done(err); - } - else { - // We expect to get one software in an array - const software = res.body; - expect(software).toBeDefined(); - expect(software.stix).toBeDefined(); - expect(software.stix.id).toBe(software1.stix.id); - expect(software.stix.modified).toBe(software1.stix.modified); - done(); - } - }); + .expect('Content-Type', /json/); + + // We expect to get one software in an array + const software = res.body; + expect(software).toBeDefined(); + expect(software.stix).toBeDefined(); + expect(software.stix.id).toBe(software1.stix.id); + expect(software.stix.modified).toBe(software1.stix.modified); + }); - it('GET /api/software/:id/modified/:modified returns the second added software', function (done) { - request(app) + it('GET /api/software/:id/modified/:modified returns the second added software', async function () { + const res = await request(app) .get('/api/software/' + software2.stix.id + '/modified/' + software2.stix.modified) .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - done(err); - } - else { - // We expect to get one software in an array - const software = res.body; - expect(software).toBeDefined(); - expect(software.stix).toBeDefined(); - expect(software.stix.id).toBe(software2.stix.id); - expect(software.stix.modified).toBe(software2.stix.modified); - done(); - } - }); + .expect('Content-Type', /json/); + + // We expect to get one software in an array + const software = res.body; + expect(software).toBeDefined(); + expect(software.stix).toBeDefined(); + expect(software.stix.id).toBe(software2.stix.id); + expect(software.stix.modified).toBe(software2.stix.modified); + }); let software3; - it('POST /api/software should create a new version of a software with a duplicate stix.id but different stix.modified date', function (done) { + it('POST /api/software should create a new version of a software with a duplicate stix.id but different stix.modified date', async function () { software3 = _.cloneDeep(software1); software3._id = undefined; software3.__t = undefined; @@ -446,122 +342,80 @@ describe('Software API', function () { const timestamp = new Date().toISOString(); software3.stix.modified = timestamp; const body = software3; - request(app) + const res = await request(app) .post('/api/software') .send(body) .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(201) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - done(err); - } - else { - // We expect to get the created software - const software = res.body; - expect(software).toBeDefined(); - done(); - } - }); + .expect('Content-Type', /json/); + + // We expect to get the created software + const software = res.body; + expect(software).toBeDefined(); + }); - it('DELETE /api/software/:id should not delete a software when the id cannot be found', function (done) { - request(app) + it('DELETE /api/software/:id should not delete a software when the id cannot be found', async function () { + const res = await request(app) .delete('/api/software/not-an-id') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) - .expect(404) - .end(function(err, res) { - if (err) { - done(err); - } - else { - done(); - } - }); + .expect(404); }); - it('DELETE /api/software/:id/modified/:modified deletes a software', function (done) { - request(app) + it('DELETE /api/software/:id/modified/:modified deletes a software', async function () { + const res = await request(app) .delete('/api/software/' + software1.stix.id + '/modified/' + software1.stix.modified) .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) - .expect(204) - .end(function(err, res) { - if (err) { - done(err); - } - else { - done(); - } - }); + .expect(204); }); - it('DELETE /api/software/:id should delete all the software with the same stix id', function (done) { - request(app) + it('DELETE /api/software/:id should delete all the software with the same stix id', async function () { + const res = await request(app) .delete('/api/software/' + software2.stix.id) .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) - .expect(204) - .end(function(err, res) { - if (err) { - done(err); - } - else { - done(); - } - }); + .expect(204); }); - it('GET /api/software returns an empty array of software', function (done) { - request(app) + it('GET /api/software returns an empty array of software', async function () { + const res = await request(app) .get('/api/software') .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - done(err); - } - else { - // We expect to get an empty array - const software = res.body; - expect(software).toBeDefined(); - expect(Array.isArray(software)).toBe(true); - expect(software.length).toBe(0); - done(); - } - }); + .expect('Content-Type', /json/); + + // We expect to get an empty array + const software = res.body; + expect(software).toBeDefined(); + expect(Array.isArray(software)).toBe(true); + expect(software.length).toBe(0); + }); - it('POST /api/software creates a software (malware) missing the is_family property using a default value', function (done) { + it('POST /api/software creates a software (malware) missing the is_family property using a default value', async function () { const timestamp = new Date().toISOString(); invalidMalwareMissingIsFamily.stix.created = timestamp; invalidMalwareMissingIsFamily.stix.modified = timestamp; const body = invalidMalwareMissingIsFamily; - request(app) + const res = await request(app) .post('/api/software') .send(body) .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(201) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - done(err); - } - else { - // We expect to get the created software - const malware = res.body; - expect(malware).toBeDefined(); - expect(malware.stix).toBeDefined(); - expect(malware.stix.id).toBeDefined(); - expect(malware.stix.created).toBeDefined(); - expect(malware.stix.modified).toBeDefined(); - expect(typeof malware.stix.is_family).toBe('boolean'); - expect(malware.stix.is_family).toBe(true); - done(); - } - }); + .expect('Content-Type', /json/); + + // We expect to get the created software + const malware = res.body; + expect(malware).toBeDefined(); + expect(malware.stix).toBeDefined(); + expect(malware.stix.id).toBeDefined(); + expect(malware.stix.created).toBeDefined(); + expect(malware.stix.modified).toBeDefined(); + expect(typeof malware.stix.is_family).toBe('boolean'); + expect(malware.stix.is_family).toBe(true); + }); after(async function() { From 66f84b13fcf292f46ba8847568102623c66080a1 Mon Sep 17 00:00:00 2001 From: Sun Date: Wed, 4 Oct 2023 15:10:25 -0400 Subject: [PATCH 03/26] refactoring software controller --- app/controllers/software-controller.js | 190 ++++++++++++------------- app/exceptions/index.js | 14 ++ app/services/software-service.js | 2 + 3 files changed, 111 insertions(+), 95 deletions(-) diff --git a/app/controllers/software-controller.js b/app/controllers/software-controller.js index c1c641e3..370986b2 100644 --- a/app/controllers/software-controller.js +++ b/app/controllers/software-controller.js @@ -2,8 +2,9 @@ const softwareService = require('../services/software-service'); const logger = require('../lib/logger'); +const { DuplicateIdError, BadlyFormattedParameterError, InvalidQueryStringParameterError, MissingPropertyError, PropertyNotAllowedError } = require('../exceptions'); -exports.retrieveAll = function(req, res) { +exports.retrieveAll = async function(req, res) { const options = { offset: req.query.offset || 0, limit: req.query.limit || 0, @@ -16,103 +17,102 @@ exports.retrieveAll = function(req, res) { lastUpdatedBy: req.query.lastUpdatedBy, includePagination: req.query.includePagination } - - softwareService.retrieveAll(options, function(err, results) { - if (err) { - logger.error('Failed with error: ' + err); - return res.status(500).send('Unable to get software. Server error.'); + try { + const res = await softwareService.retrieveAll(options); + if (options.includePagination) { + logger.debug(`Success: Retrieved ${ results.data.length } of ${ results.pagination.total } total software`); } else { - if (options.includePagination) { - logger.debug(`Success: Retrieved ${ results.data.length } of ${ results.pagination.total } total software`); - } - else { - logger.debug(`Success: Retrieved ${ results.length } software`); - } - return res.status(200).send(results); + logger.debug(`Success: Retrieved ${ results.length } software`); } - }); + return res.status(200).send(results); + } catch (err) { + logger.error('Failed with error: ' + err); + return res.status(500).send('Unable to get software. Server error.'); + } }; -exports.retrieveById = function(req, res) { +exports.retrieveById = async function(req, res) { const options = { versions: req.query.versions || 'latest' } - - softwareService.retrieveById(req.params.stixId, options, function (err, software) { - if (err) { - if (err.message === softwareService.errors.badlyFormattedParameter) { - logger.warn('Badly formatted stix id: ' + req.params.stixId); - return res.status(400).send('Stix id is badly formatted.'); - } - else if (err.message === softwareService.errors.invalidQueryStringParameter) { - logger.warn('Invalid query string: versions=' + req.query.versions); - return res.status(400).send('Query string parameter versions is invalid.'); - } - else { - logger.error('Failed with error: ' + err); - return res.status(500).send('Unable to get software. Server error.'); - } + try { + const software = await softwareService.retrieveById(req.params.stixId, options); + if (software.length === 0) { + return res.status(404).send('Software not found.'); } else { - if (software.length === 0) { - return res.status(404).send('Software not found.'); - } - else { - logger.debug(`Success: Retrieved ${ software.length } software with id ${ req.params.stixId }`); - return res.status(200).send(software); - } + logger.debug(`Success: Retrieved ${ software.length } software with id ${ req.params.stixId }`); + return res.status(200).send(software); } - }); + + } catch (err) { + if (err instanceof BadlyFormattedParameterError) { + logger.warn('Badly formatted stix id: ' + req.params.stixId); + return res.status(400).send('Stix id is badly formatted.'); + } + else if (err instanceof InvalidQueryStringParameterError) { + logger.warn('Invalid query string: versions=' + req.query.versions); + return res.status(400).send('Query string parameter versions is invalid.'); + } + else { + logger.error('Failed with error: ' + err); + return res.status(500).send('Unable to get software. Server error.'); + } + } + }; -exports.retrieveVersionById = function(req, res) { - softwareService.retrieveVersionById(req.params.stixId, req.params.modified, function (err, software) { - if (err) { - if (err.message === softwareService.errors.badlyFormattedParameter) { - logger.warn('Badly formatted stix id: ' + req.params.stixId); - return res.status(400).send('Stix id is badly formatted.'); - } - else { - logger.error('Failed with error: ' + err); - return res.status(500).send('Unable to get software. Server error.'); - } - } else { - if (!software) { - return res.status(404).send('Software not found.'); - } - else { - logger.debug(`Success: Retrieved software with id ${software.id}`); - return res.status(200).send(software); - } +exports.retrieveVersionById = async function(req, res) { + try { + const software = await softwareService.retrieveVersionById(req.params.stixId, req.params.modified); + + if (!software) { + return res.status(404).send('Software not found.'); } - }); + else { + logger.debug(`Success: Retrieved software with id ${software.id}`); + return res.status(200).send(software); + } + } catch (err) { + + if (err instanceof BadlyFormattedParameterError) { + logger.warn('Badly formatted stix id: ' + req.params.stixId); + return res.status(400).send('Stix id is badly formatted.'); + } + else { + logger.error('Failed with error: ' + err); + return res.status(500).send('Unable to get software. Server error.'); + } + } + }; exports.create = async function(req, res) { // Get the data from the request const softwareData = req.body; + const options = { + import: false, + userAccountId: req.user?.userAccountId + }; + // Create the software try { - const options = { - import: false, - userAccountId: req.user?.userAccountId - }; - const software = await softwareService.create(softwareData, options); + const software = await await softwareService.create(softwareData, options); logger.debug("Success: Created software with id " + software.stix.id); return res.status(201).send(software); } catch(err) { - if (err.message === softwareService.errors.duplicateId) { + if (err instanceof DuplicateIdError) { logger.warn("Duplicate stix.id and stix.modified"); return res.status(409).send('Unable to create software. Duplicate stix.id and stix.modified properties.'); } - else if (err.message === softwareService.errors.missingProperty) { + else if (err instanceof MissingPropertyError) { logger.warn(`Unable to create software, missing property ${ err.propertyName }`); return res.status(400).send(`Unable to create software, missing property ${ err.propertyName }`); } - else if (err.message === softwareService.errors.propertyNotAllowed) { + else if (err instanceof PropertyNotAllowedError) { logger.warn(`Unable to create software, property ${ err.propertyName } is not allowed`); return res.status(400).send(`Unable to create software, property ${ err.propertyName } is not allowed`); } @@ -123,12 +123,12 @@ exports.create = async function(req, res) { } }; -exports.updateFull = function(req, res) { +exports.updateFull = async function(req, res) { // Get the data from the request const softwareData = req.body; // Create the software - softwareService.updateFull(req.params.stixId, req.params.modified, softwareData, function(err, software) { + await softwareService.updateFull(req.params.stixId, req.params.modified, softwareData, async function(err, software) { if (err) { logger.error("Failed with error: " + err); return res.status(500).send("Unable to update software. Server error."); @@ -144,37 +144,37 @@ exports.updateFull = function(req, res) { }); }; -exports.deleteVersionById = function(req, res) { - softwareService.deleteVersionById(req.params.stixId, req.params.modified, function (err, software) { - if (err) { - logger.error('Delete software failed. ' + err); - return res.status(500).send('Unable to delete software. Server error.'); - } - else { - if (!software) { - return res.status(404).send('Software not found.'); - } else { - logger.debug("Success: Deleted software with id " + software.stix.id); - return res.status(204).end(); - } +exports.deleteVersionById = async function(req, res) { + try { + const software = await softwareService.deleteVersionById(req.params.stixId, req.params.modified); + + if (!software) { + return res.status(404).send('Software not found.'); + } else { + logger.debug("Success: Deleted software with id " + software.stix.id); + return res.status(204).end(); } - }); + + } catch (err) { + logger.error('Delete software failed. ' + err); + return res.status(500).send('Unable to delete software. Server error.'); + } }; -exports.deleteById = function(req, res) { - softwareService.deleteById(req.params.stixId, function (err, softwares) { - if (err) { - logger.error('Delete software failed. ' + err); - return res.status(500).send('Unable to delete software. Server error.'); +exports.deleteById = async function(req, res) { + try { + const softwares = await softwareService.deleteById(req.params.stixId); + + if (softwares.deletedCount === 0) { + return res.status(404).send('Software not found.'); } else { - if (softwares.deletedCount === 0) { - return res.status(404).send('Software not found.'); - } - else { - logger.debug(`Success: Deleted software with id ${ req.params.stixId }`); - return res.status(204).end(); - } + logger.debug(`Success: Deleted software with id ${ req.params.stixId }`); + return res.status(204).end(); } - }); + + } catch (err) { + logger.error('Delete software failed. ' + err); + return res.status(500).send('Unable to delete software. Server error.'); + } }; diff --git a/app/exceptions/index.js b/app/exceptions/index.js index d940e82d..cd486092 100644 --- a/app/exceptions/index.js +++ b/app/exceptions/index.js @@ -81,6 +81,18 @@ class NotImplementedError extends CustomError { } } +class MissingPropertyError extends CustomError { + constructor(propertyName, options) { + super(`Unable to create software, missing property ${propertyName}`, options); + } +} + +class PropertyNotAllowedError extends CustomError { + constructor(propertyName, options) { + super(`Unable to create software, property ${propertyName} is not allowed`, options); + } +} + module.exports = { //** General errors */ @@ -101,4 +113,6 @@ module.exports = { IdentityServiceError, TechniquesServiceError, TacticsServiceError, + MissingPropertyError, + PropertyNotAllowedError, }; diff --git a/app/services/software-service.js b/app/services/software-service.js index 9d6a056f..a75cd4ed 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -30,3 +30,5 @@ class SoftwareService extends BaseService { } } + +module.exports = new SoftwareService(); \ No newline at end of file From f6cf7065a197f35fcf0c48c4c0b9b3630f01d7aa Mon Sep 17 00:00:00 2001 From: Sun Date: Wed, 4 Oct 2023 15:33:58 -0400 Subject: [PATCH 04/26] removed an unnecessary parameter --- app/services/software-service.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/services/software-service.js b/app/services/software-service.js index a75cd4ed..392cdc02 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -26,7 +26,6 @@ const SoftwareRepository = require('../repository/software-repository'); class SoftwareService extends BaseService { constructor () { super(SoftwareRepository, Software); - this.retrieveAllTechniques = null; } } From 52e6b98a02de0a05c60654c6246c29f37687637b Mon Sep 17 00:00:00 2001 From: Sun Date: Wed, 4 Oct 2023 15:36:54 -0400 Subject: [PATCH 05/26] changed one more controller piece --- app/controllers/software-controller.js | 30 ++++++++++++-------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/app/controllers/software-controller.js b/app/controllers/software-controller.js index 370986b2..233a449c 100644 --- a/app/controllers/software-controller.js +++ b/app/controllers/software-controller.js @@ -102,8 +102,7 @@ exports.create = async function(req, res) { const software = await await softwareService.create(softwareData, options); logger.debug("Success: Created software with id " + software.stix.id); return res.status(201).send(software); - } - catch(err) { + } catch(err) { if (err instanceof DuplicateIdError) { logger.warn("Duplicate stix.id and stix.modified"); return res.status(409).send('Unable to create software. Duplicate stix.id and stix.modified properties.'); @@ -127,21 +126,20 @@ exports.updateFull = async function(req, res) { // Get the data from the request const softwareData = req.body; - // Create the software - await softwareService.updateFull(req.params.stixId, req.params.modified, softwareData, async function(err, software) { - if (err) { - logger.error("Failed with error: " + err); - return res.status(500).send("Unable to update software. Server error."); + try { + // Create the software + const software = await softwareService.updateFull(req.params.stixId, req.params.modified, softwareData); + + if (!software) { + return res.status(404).send('Software not found.'); + } else { + logger.debug("Success: Updated software with id " + software.stix.id); + return res.status(200).send(software); } - else { - if (!software) { - return res.status(404).send('Software not found.'); - } else { - logger.debug("Success: Updated software with id " + software.stix.id); - return res.status(200).send(software); - } - } - }); + } catch (err) { + logger.error("Failed with error: " + err); + return res.status(500).send("Unable to update software. Server error."); + } }; exports.deleteVersionById = async function(req, res) { From b07439355e034f3e50ae2f5b108e0c35b4f6b782 Mon Sep 17 00:00:00 2001 From: Sun Date: Wed, 4 Oct 2023 17:14:08 -0400 Subject: [PATCH 06/26] fixed error enum for software service --- app/services/software-service.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/app/services/software-service.js b/app/services/software-service.js index 392cdc02..cd70e847 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -9,16 +9,7 @@ const config = require('../config/config'); const regexValidator = require('../lib/regex'); const {lastUpdatedByQueryHelper} = require('../lib/request-parameter-helper'); -const errors = { - missingParameter: 'Missing required parameter', - missingProperty: 'Missing required property', - propertyNotAllowed: 'Includes property that is not allowed', - badlyFormattedParameter: 'Badly formatted parameter', - duplicateId: 'Duplicate id', - notFound: 'Document not found', - invalidQueryStringParameter: 'Invalid query string parameter' -}; -exports.errors = errors; +const { MissingParameterError, MissingPropertyError, PropertyNotAllowedError, BadlyFormattedParameterError, DuplicateIdError, NotFoundError, InvalidQueryStringParameterError } = require('../exceptions'); const BaseService = require('./_base.service'); const SoftwareRepository = require('../repository/software-repository'); From 143375bbc0969adb93b388999beb300cdae9ea22 Mon Sep 17 00:00:00 2001 From: Sun Date: Thu, 5 Oct 2023 12:43:01 -0400 Subject: [PATCH 07/26] removing assignments where unneeded --- app/tests/api/software/software.spec.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/tests/api/software/software.spec.js b/app/tests/api/software/software.spec.js index da92ed53..16f1f77d 100644 --- a/app/tests/api/software/software.spec.js +++ b/app/tests/api/software/software.spec.js @@ -95,7 +95,7 @@ describe('Software API', function () { it('POST /api/software does not create an empty software', async function () { const body = { }; - const res = await request(app) + await request(app) .post('/api/software') .send(body) .set('Accept', 'application/json') @@ -108,7 +108,7 @@ describe('Software API', function () { invalidMissingName.stix.created = timestamp; invalidMissingName.stix.modified = timestamp; const body = invalidMissingName; - const res = await request(app) + await request(app) .post('/api/software') .send(body) .set('Accept', 'application/json') @@ -121,7 +121,7 @@ describe('Software API', function () { invalidToolIncludesIsFamily.stix.created = timestamp; invalidToolIncludesIsFamily.stix.modified = timestamp; const body = invalidToolIncludesIsFamily; - const res = await request(app) + await request(app) .post('/api/software') .send(body) .set('Accept', 'application/json') @@ -170,7 +170,7 @@ describe('Software API', function () { }); it('GET /api/software/:id should not return a software when the id cannot be found', async function () { - const res = await request(app) + await request(app) .get('/api/software/not-an-id') .set('Accept', 'application/json') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) @@ -234,7 +234,7 @@ describe('Software API', function () { it('POST /api/software does not create a software with the same id and modified date', async function () { const body = software1; - const res = await request(app) + await request(app) .post('/api/software') .send(body) .set('Accept', 'application/json') @@ -357,21 +357,21 @@ describe('Software API', function () { }); it('DELETE /api/software/:id should not delete a software when the id cannot be found', async function () { - const res = await request(app) + await request(app) .delete('/api/software/not-an-id') .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(404); }); it('DELETE /api/software/:id/modified/:modified deletes a software', async function () { - const res = await request(app) + await request(app) .delete('/api/software/' + software1.stix.id + '/modified/' + software1.stix.modified) .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(204); }); it('DELETE /api/software/:id should delete all the software with the same stix id', async function () { - const res = await request(app) + await request(app) .delete('/api/software/' + software2.stix.id) .set('Cookie', `${ login.passportCookieName }=${ passportCookie.value }`) .expect(204); From abb30abd06045bde921488e06b9fcfe2dc2ae604 Mon Sep 17 00:00:00 2001 From: Sun Date: Fri, 6 Oct 2023 11:50:08 -0400 Subject: [PATCH 08/26] fixed results in software controller --- app/controllers/software-controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/software-controller.js b/app/controllers/software-controller.js index 233a449c..f27a2ecc 100644 --- a/app/controllers/software-controller.js +++ b/app/controllers/software-controller.js @@ -18,7 +18,7 @@ exports.retrieveAll = async function(req, res) { includePagination: req.query.includePagination } try { - const res = await softwareService.retrieveAll(options); + const results = await softwareService.retrieveAll(options); if (options.includePagination) { logger.debug(`Success: Retrieved ${ results.data.length } of ${ results.pagination.total } total software`); } From f341c1dbba642a2f95bd57e94256cc3f55946e1d Mon Sep 17 00:00:00 2001 From: Sun Date: Fri, 6 Oct 2023 12:11:31 -0400 Subject: [PATCH 09/26] passed all tests --- app/services/software-service.js | 77 ++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/app/services/software-service.js b/app/services/software-service.js index cd70e847..2ea58503 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -19,6 +19,83 @@ class SoftwareService extends BaseService { super(SoftwareRepository, Software); } + async create(data, options) { + // This function handles two use cases: + // 1. This is a completely new object. Create a new object and generate the stix.id if not already + // provided. Set both stix.created_by_ref and stix.x_mitre_modified_by_ref to the organization identity. + // 2. This is a new version of an existing object. Create a new object with the specified id. + // Set stix.x_mitre_modified_by_ref to the organization identity. + + // is_family defaults to true for malware, not allowed for tools + if (data.stix && data.stix.type === 'malware' && typeof data.stix.is_family !== 'boolean') { + data.stix.is_family = true; + } + else if (data.stix && data.stix.type === 'tool' && data.stix.is_family !== undefined) { + throw new PropertyNotAllowedError; + } + + // Create the document + const software = new Software(data); + + options = options || {}; + if (!options.import) { + // Set the ATT&CK Spec Version + software.stix.x_mitre_attack_spec_version = software.stix.x_mitre_attack_spec_version ?? config.app.attackSpecVersion; + + // Record the user account that created the object + if (options.userAccountId) { + software.workspace.workflow.created_by_user_account = options.userAccountId; + } + + // Set the default marking definitions + await attackObjectsService.setDefaultMarkingDefinitions(software); + + // Get the organization identity + const organizationIdentityRef = await systemConfigurationService.retrieveOrganizationIdentityRef(); + + // Check for an existing object + let existingObject; + if (software.stix.id) { + existingObject = await Software.findOne({ 'stix.id': software.stix.id }); + } + + if (existingObject) { + // New version of an existing object + // Only set the x_mitre_modified_by_ref property + software.stix.x_mitre_modified_by_ref = organizationIdentityRef; + } + else { + // New object + // Assign a new STIX id if not already provided + if (software.stix.type === 'tool') { + software.stix.id = software.stix.id || `tool--${uuid.v4()}`; + } + else { + software.stix.id = software.stix.id || `malware--${uuid.v4()}`; + } + + // Set the created_by_ref and x_mitre_modified_by_ref properties + software.stix.created_by_ref = organizationIdentityRef; + software.stix.x_mitre_modified_by_ref = organizationIdentityRef; + } + } + + // Save the document in the database + try { + const savedSoftware = await software.save(); + return savedSoftware; + } + catch(err) { + if (err.name === 'MongoServerError' && err.code === 11000) { + // 11000 = Duplicate index + throw new DuplicateIdError; + } + else { + throw err; + } + } + }; + } module.exports = new SoftwareService(); \ No newline at end of file From 4348d217a698b36e070374fb36752cedc09387c9 Mon Sep 17 00:00:00 2001 From: Sun Date: Thu, 12 Oct 2023 15:25:14 -0400 Subject: [PATCH 10/26] fixing lint issues --- app/services/software-service.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/services/software-service.js b/app/services/software-service.js index 2ea58503..482deb66 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -3,13 +3,10 @@ const uuid = require('uuid'); const Software = require('../models/software-model'); const systemConfigurationService = require('./system-configuration-service'); -const identitiesService = require('./identities-service'); const attackObjectsService = require('./attack-objects-service'); const config = require('../config/config'); -const regexValidator = require('../lib/regex'); -const {lastUpdatedByQueryHelper} = require('../lib/request-parameter-helper'); -const { MissingParameterError, MissingPropertyError, PropertyNotAllowedError, BadlyFormattedParameterError, DuplicateIdError, NotFoundError, InvalidQueryStringParameterError } = require('../exceptions'); +const { PropertyNotAllowedError, DuplicateIdError } = require('../exceptions'); const BaseService = require('./_base.service'); const SoftwareRepository = require('../repository/software-repository'); @@ -94,7 +91,7 @@ class SoftwareService extends BaseService { throw err; } } - }; + } } From ddb3dca918b975df2acdf3e5d673f19e81b18348 Mon Sep 17 00:00:00 2001 From: Sun Date: Mon, 16 Oct 2023 13:26:24 -0400 Subject: [PATCH 11/26] making things async --- app/services/software-service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/software-service.js b/app/services/software-service.js index 482deb66..b0b89272 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -16,7 +16,7 @@ class SoftwareService extends BaseService { super(SoftwareRepository, Software); } - async create(data, options) { + static async create(data, options) { // This function handles two use cases: // 1. This is a completely new object. Create a new object and generate the stix.id if not already // provided. Set both stix.created_by_ref and stix.x_mitre_modified_by_ref to the organization identity. From fafed49e7bceb03cba0e5ebecdd0a3415287eedc Mon Sep 17 00:00:00 2001 From: Sun Date: Tue, 5 Dec 2023 14:36:22 -0500 Subject: [PATCH 12/26] starting to change software service --- app/services/software-service.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/services/software-service.js b/app/services/software-service.js index b0b89272..9ff2147a 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -12,9 +12,6 @@ const BaseService = require('./_base.service'); const SoftwareRepository = require('../repository/software-repository'); class SoftwareService extends BaseService { - constructor () { - super(SoftwareRepository, Software); - } static async create(data, options) { // This function handles two use cases: @@ -95,4 +92,4 @@ class SoftwareService extends BaseService { } -module.exports = new SoftwareService(); \ No newline at end of file +module.exports = new SoftwareService(SoftwareRepository); \ No newline at end of file From 38333475c595922006cba518249e57f0ffd05ddc Mon Sep 17 00:00:00 2001 From: Sun Date: Wed, 6 Dec 2023 15:41:46 -0500 Subject: [PATCH 13/26] aligning with refactor --- app/exceptions/index.js | 5 +---- app/services/software-service.js | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/exceptions/index.js b/app/exceptions/index.js index c641cc25..d8b6e3b4 100644 --- a/app/exceptions/index.js +++ b/app/exceptions/index.js @@ -117,10 +117,7 @@ module.exports = { IdentityServiceError, TechniquesServiceError, TacticsServiceError, -<<<<<<< HEAD MissingPropertyError, PropertyNotAllowedError, -======= - InvalidTypeError ->>>>>>> project-orion + InvalidTypeError, }; diff --git a/app/services/software-service.js b/app/services/software-service.js index 9ff2147a..3cbf97ae 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -92,4 +92,4 @@ class SoftwareService extends BaseService { } -module.exports = new SoftwareService(SoftwareRepository); \ No newline at end of file +module.exports = new SoftwareService(null, SoftwareRepository); \ No newline at end of file From 586c677660292243e3eac340ed1da1006c96c7fd Mon Sep 17 00:00:00 2001 From: Sun Date: Wed, 6 Dec 2023 15:54:55 -0500 Subject: [PATCH 14/26] redoing repo --- app/exceptions/index.js | 2 ++ app/repository/software-repository.js | 5 +---- app/services/software-service.js | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/exceptions/index.js b/app/exceptions/index.js index d8b6e3b4..47a48817 100644 --- a/app/exceptions/index.js +++ b/app/exceptions/index.js @@ -90,6 +90,8 @@ class MissingPropertyError extends CustomError { class PropertyNotAllowedError extends CustomError { constructor(propertyName, options) { super(`Unable to create software, property ${propertyName} is not allowed`, options); + } +} class InvalidTypeError extends CustomError { constructor(options) { diff --git a/app/repository/software-repository.js b/app/repository/software-repository.js index 4c7fccad..80fe0680 100644 --- a/app/repository/software-repository.js +++ b/app/repository/software-repository.js @@ -5,9 +5,6 @@ const Software = require('../models/software-model'); class SoftwareRepository extends BaseRepository { - constructor() { - super(Software); - } } -module.exports = new SoftwareRepository(); \ No newline at end of file +module.exports = new SoftwareRepository(Software); \ No newline at end of file diff --git a/app/services/software-service.js b/app/services/software-service.js index 3cbf97ae..f96979a5 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -92,4 +92,4 @@ class SoftwareService extends BaseService { } -module.exports = new SoftwareService(null, SoftwareRepository); \ No newline at end of file +module.exports = new SoftwareService('tool', SoftwareRepository); \ No newline at end of file From 2b2dda639fe4d134e1d0bbc5a24314c04bde3cbc Mon Sep 17 00:00:00 2001 From: Sun Date: Mon, 18 Dec 2023 16:40:47 -0500 Subject: [PATCH 15/26] trying to make changes --- app/services/_base.service.js | 7 +++- app/services/software-service.js | 66 ++------------------------------ 2 files changed, 9 insertions(+), 64 deletions(-) diff --git a/app/services/_base.service.js b/app/services/_base.service.js index 41a907fb..41822a69 100644 --- a/app/services/_base.service.js +++ b/app/services/_base.service.js @@ -219,8 +219,11 @@ class BaseService extends AbstractService { callback = arguments[arguments.length - 1]; } - if (data?.stix?.type !== this.type) { - throw new InvalidTypeError(); + if (data?.stix?.type != null) { + + if (data?.stix?.type !== this.type) { + throw new InvalidTypeError(); + } } // eslint-disable-next-line no-useless-catch diff --git a/app/services/software-service.js b/app/services/software-service.js index f96979a5..df0ec0fc 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -27,69 +27,11 @@ class SoftwareService extends BaseService { else if (data.stix && data.stix.type === 'tool' && data.stix.is_family !== undefined) { throw new PropertyNotAllowedError; } - - // Create the document - const software = new Software(data); - - options = options || {}; - if (!options.import) { - // Set the ATT&CK Spec Version - software.stix.x_mitre_attack_spec_version = software.stix.x_mitre_attack_spec_version ?? config.app.attackSpecVersion; - - // Record the user account that created the object - if (options.userAccountId) { - software.workspace.workflow.created_by_user_account = options.userAccountId; - } - - // Set the default marking definitions - await attackObjectsService.setDefaultMarkingDefinitions(software); - - // Get the organization identity - const organizationIdentityRef = await systemConfigurationService.retrieveOrganizationIdentityRef(); - - // Check for an existing object - let existingObject; - if (software.stix.id) { - existingObject = await Software.findOne({ 'stix.id': software.stix.id }); - } - - if (existingObject) { - // New version of an existing object - // Only set the x_mitre_modified_by_ref property - software.stix.x_mitre_modified_by_ref = organizationIdentityRef; - } - else { - // New object - // Assign a new STIX id if not already provided - if (software.stix.type === 'tool') { - software.stix.id = software.stix.id || `tool--${uuid.v4()}`; - } - else { - software.stix.id = software.stix.id || `malware--${uuid.v4()}`; - } - - // Set the created_by_ref and x_mitre_modified_by_ref properties - software.stix.created_by_ref = organizationIdentityRef; - software.stix.x_mitre_modified_by_ref = organizationIdentityRef; - } - } - - // Save the document in the database - try { - const savedSoftware = await software.save(); - return savedSoftware; - } - catch(err) { - if (err.name === 'MongoServerError' && err.code === 11000) { - // 11000 = Duplicate index - throw new DuplicateIdError; - } - else { - throw err; - } - } + + super.create(data, options); + } } -module.exports = new SoftwareService('tool', SoftwareRepository); \ No newline at end of file +module.exports = new SoftwareService(null, SoftwareRepository); \ No newline at end of file From c28f8d4ffab9b468732c08e974b7343cacd60e0c Mon Sep 17 00:00:00 2001 From: Sun Date: Tue, 19 Dec 2023 16:59:31 -0500 Subject: [PATCH 16/26] removing duplicate code --- app/services/software-service.js | 37 ++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/app/services/software-service.js b/app/services/software-service.js index df0ec0fc..15d7ac20 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -10,6 +10,7 @@ const { PropertyNotAllowedError, DuplicateIdError } = require('../exceptions'); const BaseService = require('./_base.service'); const SoftwareRepository = require('../repository/software-repository'); +const softwareRepository = require('../repository/software-repository'); class SoftwareService extends BaseService { @@ -27,9 +28,41 @@ class SoftwareService extends BaseService { else if (data.stix && data.stix.type === 'tool' && data.stix.is_family !== undefined) { throw new PropertyNotAllowedError; } - - super.create(data, options); + + if (existingObject) { + // New version of an existing object + // Only set the x_mitre_modified_by_ref property + data.x_mitre_modified_by_ref = organizationIdentityRef; + } + else { + // New object + // Assign a new STIX id if not already provided + if (data.type === 'tool') { + data.id = data.id || `tool--${uuid.v4()}`; + } + else { + data.id = data.id || `malware--${uuid.v4()}`; + } + // Set the created_by_ref and x_mitre_modified_by_ref properties + data.created_by_ref = organizationIdentityRef; + data.x_mitre_modified_by_ref = organizationIdentityRef; + } + + // Save the document in the database + try { + const savedSoftware = await softwareRepository.save(data); + return savedSoftware; + } + catch(err) { + if (err.name === 'MongoServerError' && err.code === 11000) { + // 11000 = Duplicate index + throw new DuplicateIdError; + } + else { + throw err; + } + } } } From d9e0c67ab0a311fe6997c721ac784b0992241d60 Mon Sep 17 00:00:00 2001 From: Sun Date: Thu, 28 Dec 2023 09:58:29 -0500 Subject: [PATCH 17/26] trying to get this to work with 2 stix types --- app/services/_base.service.js | 7 +++---- app/services/software-service.js | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/services/_base.service.js b/app/services/_base.service.js index 41822a69..2f67cca1 100644 --- a/app/services/_base.service.js +++ b/app/services/_base.service.js @@ -219,12 +219,11 @@ class BaseService extends AbstractService { callback = arguments[arguments.length - 1]; } - if (data?.stix?.type != null) { - if (data?.stix?.type !== this.type) { - throw new InvalidTypeError(); - } + if (data?.stix?.type !== this.type) { + throw new InvalidTypeError(); } + // eslint-disable-next-line no-useless-catch try { diff --git a/app/services/software-service.js b/app/services/software-service.js index 15d7ac20..1183d8c1 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -51,7 +51,7 @@ class SoftwareService extends BaseService { // Save the document in the database try { - const savedSoftware = await softwareRepository.save(data); + const savedSoftware = await super.create(data, options); return savedSoftware; } catch(err) { From f79290d0a28aa436afcf4e87dfec7587003a575b Mon Sep 17 00:00:00 2001 From: Sun Date: Tue, 9 Jan 2024 19:29:45 -0500 Subject: [PATCH 18/26] debugging --- app/controllers/software-controller.js | 14 ++++++++++++++ app/services/software-service.js | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/controllers/software-controller.js b/app/controllers/software-controller.js index f27a2ecc..010b6ce4 100644 --- a/app/controllers/software-controller.js +++ b/app/controllers/software-controller.js @@ -27,6 +27,8 @@ exports.retrieveAll = async function(req, res) { } return res.status(200).send(results); } catch (err) { + console.log("retrieve all error"); + console.log(err); logger.error('Failed with error: ' + err); return res.status(500).send('Unable to get software. Server error.'); } @@ -56,6 +58,8 @@ exports.retrieveById = async function(req, res) { return res.status(400).send('Query string parameter versions is invalid.'); } else { + console.log("retrieve by id error"); + console.log(err); logger.error('Failed with error: ' + err); return res.status(500).send('Unable to get software. Server error.'); } @@ -81,6 +85,8 @@ exports.retrieveVersionById = async function(req, res) { return res.status(400).send('Stix id is badly formatted.'); } else { + console.log("retrieve version by id error"); + console.log(err); logger.error('Failed with error: ' + err); return res.status(500).send('Unable to get software. Server error.'); } @@ -116,6 +122,8 @@ exports.create = async function(req, res) { return res.status(400).send(`Unable to create software, property ${ err.propertyName } is not allowed`); } else { + console.log("create error"); + console.log(err); logger.error("Failed with error: " + err); return res.status(500).send("Unable to create software. Server error."); } @@ -137,6 +145,8 @@ exports.updateFull = async function(req, res) { return res.status(200).send(software); } } catch (err) { + console.log("update full error"); + console.log(err); logger.error("Failed with error: " + err); return res.status(500).send("Unable to update software. Server error."); } @@ -154,6 +164,8 @@ exports.deleteVersionById = async function(req, res) { } } catch (err) { + console.log("delete version by id error"); + console.log(err); logger.error('Delete software failed. ' + err); return res.status(500).send('Unable to delete software. Server error.'); } @@ -172,6 +184,8 @@ exports.deleteById = async function(req, res) { } } catch (err) { + console.log("delete by id error"); + console.log(err); logger.error('Delete software failed. ' + err); return res.status(500).send('Unable to delete software. Server error.'); } diff --git a/app/services/software-service.js b/app/services/software-service.js index 1183d8c1..02284415 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -14,7 +14,7 @@ const softwareRepository = require('../repository/software-repository'); class SoftwareService extends BaseService { - static async create(data, options) { + async create(data, options) { // This function handles two use cases: // 1. This is a completely new object. Create a new object and generate the stix.id if not already // provided. Set both stix.created_by_ref and stix.x_mitre_modified_by_ref to the organization identity. From 18ac0cf2f3c0020123bd0fa4661f530b139f4d80 Mon Sep 17 00:00:00 2001 From: Sun Date: Tue, 9 Jan 2024 19:31:52 -0500 Subject: [PATCH 19/26] almost there --- app/services/software-service.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/services/software-service.js b/app/services/software-service.js index 02284415..8dd15590 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -10,7 +10,6 @@ const { PropertyNotAllowedError, DuplicateIdError } = require('../exceptions'); const BaseService = require('./_base.service'); const SoftwareRepository = require('../repository/software-repository'); -const softwareRepository = require('../repository/software-repository'); class SoftwareService extends BaseService { @@ -28,6 +27,11 @@ class SoftwareService extends BaseService { else if (data.stix && data.stix.type === 'tool' && data.stix.is_family !== undefined) { throw new PropertyNotAllowedError; } + + let existingObject; + if (data.stix.id) { + existingObject = await Software.findOne({ 'stix.id': data.stix.id }); + } if (existingObject) { // New version of an existing object From ca5bb0a7a176b86b710acc5db5573cdeaebe5127 Mon Sep 17 00:00:00 2001 From: Sun Date: Tue, 9 Jan 2024 19:34:21 -0500 Subject: [PATCH 20/26] even closer --- app/services/software-service.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/services/software-service.js b/app/services/software-service.js index 8dd15590..eede822d 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -28,6 +28,8 @@ class SoftwareService extends BaseService { throw new PropertyNotAllowedError; } + const organizationIdentityRef = await systemConfigurationService.retrieveOrganizationIdentityRef(); + let existingObject; if (data.stix.id) { existingObject = await Software.findOne({ 'stix.id': data.stix.id }); From 45f257ad18491cd49c11fb01b2a723e6a3f7775e Mon Sep 17 00:00:00 2001 From: Sun Date: Tue, 9 Jan 2024 19:38:08 -0500 Subject: [PATCH 21/26] trying to re-do create function --- app/services/software-service.js | 60 ++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/app/services/software-service.js b/app/services/software-service.js index eede822d..a959e9ff 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -28,33 +28,48 @@ class SoftwareService extends BaseService { throw new PropertyNotAllowedError; } - const organizationIdentityRef = await systemConfigurationService.retrieveOrganizationIdentityRef(); - - let existingObject; - if (data.stix.id) { - existingObject = await Software.findOne({ 'stix.id': data.stix.id }); - } + options = options || {}; + if (!options.import) { + // Set the ATT&CK Spec Version + data.stix.x_mitre_attack_spec_version = data.stix.x_mitre_attack_spec_version ?? config.app.attackSpecVersion; - if (existingObject) { - // New version of an existing object - // Only set the x_mitre_modified_by_ref property - data.x_mitre_modified_by_ref = organizationIdentityRef; - } - else { - // New object - // Assign a new STIX id if not already provided - if (data.type === 'tool') { - data.id = data.id || `tool--${uuid.v4()}`; + // Record the user account that created the object + if (options.userAccountId) { + data.workspace.workflow.created_by_user_account = options.userAccountId; + } + + // Set the default marking definitions + await attackObjectsService.setDefaultMarkingDefinitions(data); + + // Get the organization identity + const organizationIdentityRef = await systemConfigurationService.retrieveOrganizationIdentityRef(); + + // Check for an existing object + let existingObject; + if (data.stix.id) { + existingObject = await Software.findOne({ 'stix.id': data.stix.id }); + } + + if (existingObject) { + // New version of an existing object + // Only set the x_mitre_modified_by_ref property + data.stix.x_mitre_modified_by_ref = organizationIdentityRef; } else { - data.id = data.id || `malware--${uuid.v4()}`; + // New object + // Assign a new STIX id if not already provided + if (data.stix.type === 'tool') { + data.stix.id = data.stix.id || `tool--${uuid.v4()}`; + } + else { + data.stix.id = data.stix.id || `malware--${uuid.v4()}`; + } + + // Set the created_by_ref and x_mitre_modified_by_ref properties + data.stix.created_by_ref = organizationIdentityRef; + data.stix.x_mitre_modified_by_ref = organizationIdentityRef; } - // Set the created_by_ref and x_mitre_modified_by_ref properties - data.created_by_ref = organizationIdentityRef; - data.x_mitre_modified_by_ref = organizationIdentityRef; - } - // Save the document in the database try { const savedSoftware = await super.create(data, options); @@ -72,5 +87,6 @@ class SoftwareService extends BaseService { } } +} module.exports = new SoftwareService(null, SoftwareRepository); \ No newline at end of file From 6a981411275f0240125d79a15b8b08805a5aa27e Mon Sep 17 00:00:00 2001 From: Sun Date: Tue, 9 Jan 2024 19:53:49 -0500 Subject: [PATCH 22/26] trying final things --- app/services/software-service.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/services/software-service.js b/app/services/software-service.js index a959e9ff..e32ad25f 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -72,6 +72,7 @@ class SoftwareService extends BaseService { // Save the document in the database try { + console.log(data.stix.type); const savedSoftware = await super.create(data, options); return savedSoftware; } From 5620d59e159ec4101877d708ab1045d6b2570c18 Mon Sep 17 00:00:00 2001 From: Sun Date: Thu, 11 Jan 2024 11:46:05 -0500 Subject: [PATCH 23/26] fixing lots of tests --- app/services/_base.service.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/services/_base.service.js b/app/services/_base.service.js index 2f67cca1..e2445a6a 100644 --- a/app/services/_base.service.js +++ b/app/services/_base.service.js @@ -220,9 +220,9 @@ class BaseService extends AbstractService { } - if (data?.stix?.type !== this.type) { - throw new InvalidTypeError(); - } + // if (data?.stix?.type !== this.type) { + // throw new InvalidTypeError(); + // } // eslint-disable-next-line no-useless-catch From 8b3af3e6d7a6050489f6f5598e75995ca3658ac0 Mon Sep 17 00:00:00 2001 From: Sun Date: Thu, 11 Jan 2024 11:50:58 -0500 Subject: [PATCH 24/26] resolving more errors --- app/services/_base.service.js | 6 +-- app/services/software-service.js | 82 ++++++++++++++------------------ 2 files changed, 40 insertions(+), 48 deletions(-) diff --git a/app/services/_base.service.js b/app/services/_base.service.js index e2445a6a..2f67cca1 100644 --- a/app/services/_base.service.js +++ b/app/services/_base.service.js @@ -220,9 +220,9 @@ class BaseService extends AbstractService { } - // if (data?.stix?.type !== this.type) { - // throw new InvalidTypeError(); - // } + if (data?.stix?.type !== this.type) { + throw new InvalidTypeError(); + } // eslint-disable-next-line no-useless-catch diff --git a/app/services/software-service.js b/app/services/software-service.js index e32ad25f..b8db45ca 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -13,7 +13,7 @@ const SoftwareRepository = require('../repository/software-repository'); class SoftwareService extends BaseService { - async create(data, options) { + async create(data, options, callback) { // This function handles two use cases: // 1. This is a completely new object. Create a new object and generate the stix.id if not already // provided. Set both stix.created_by_ref and stix.x_mitre_modified_by_ref to the organization identity. @@ -37,57 +37,49 @@ class SoftwareService extends BaseService { if (options.userAccountId) { data.workspace.workflow.created_by_user_account = options.userAccountId; } - + // Set the default marking definitions await attackObjectsService.setDefaultMarkingDefinitions(data); - + // Get the organization identity const organizationIdentityRef = await systemConfigurationService.retrieveOrganizationIdentityRef(); - // Check for an existing object - let existingObject; - if (data.stix.id) { - existingObject = await Software.findOne({ 'stix.id': data.stix.id }); - } - - if (existingObject) { - // New version of an existing object - // Only set the x_mitre_modified_by_ref property - data.stix.x_mitre_modified_by_ref = organizationIdentityRef; - } - else { - // New object - // Assign a new STIX id if not already provided - if (data.stix.type === 'tool') { - data.stix.id = data.stix.id || `tool--${uuid.v4()}`; - } - else { - data.stix.id = data.stix.id || `malware--${uuid.v4()}`; - } - - // Set the created_by_ref and x_mitre_modified_by_ref properties - data.stix.created_by_ref = organizationIdentityRef; - data.stix.x_mitre_modified_by_ref = organizationIdentityRef; - } + // Check for an existing object + let existingObject; + if (data.stix.id) { + existingObject = await this.repository.retrieveOneById(data.stix.id); + } - // Save the document in the database - try { - console.log(data.stix.type); - const savedSoftware = await super.create(data, options); - return savedSoftware; - } - catch(err) { - if (err.name === 'MongoServerError' && err.code === 11000) { - // 11000 = Duplicate index - throw new DuplicateIdError; - } - else { - throw err; - } - } - } + if (existingObject) { + // New version of an existing object + // Only set the x_mitre_modified_by_ref property + data.stix.x_mitre_modified_by_ref = organizationIdentityRef; + } + else { + // New object + // Assign a new STIX id if not already provided + if (!data.stix.id) { + // const stixIdPrefix = getStixIdPrefixFromModel(this.model.modelName, data.stix.type); + data.stix.id = `${data.stix.type}--${uuid.v4()}`; + } + // Set the created_by_ref and x_mitre_modified_by_ref properties + data.stix.created_by_ref = organizationIdentityRef; + data.stix.x_mitre_modified_by_ref = organizationIdentityRef; + } + } + const res = await this.repository.save(data); + if (callback) { + return callback(null, res); + } + return res; + } catch (err) { + if (callback) { + return callback(err); + } + throw err; + } } -} + module.exports = new SoftwareService(null, SoftwareRepository); \ No newline at end of file From a1594e16b83c606b459a08f3d17f5338272b36c2 Mon Sep 17 00:00:00 2001 From: Sun Date: Thu, 11 Jan 2024 11:52:53 -0500 Subject: [PATCH 25/26] making syntax checks --- app/services/software-service.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/services/software-service.js b/app/services/software-service.js index b8db45ca..3019a996 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -1,12 +1,11 @@ 'use strict'; const uuid = require('uuid'); -const Software = require('../models/software-model'); const systemConfigurationService = require('./system-configuration-service'); const attackObjectsService = require('./attack-objects-service'); const config = require('../config/config'); -const { PropertyNotAllowedError, DuplicateIdError } = require('../exceptions'); +const { PropertyNotAllowedError} = require('../exceptions'); const BaseService = require('./_base.service'); const SoftwareRepository = require('../repository/software-repository'); @@ -21,6 +20,7 @@ class SoftwareService extends BaseService { // Set stix.x_mitre_modified_by_ref to the organization identity. // is_family defaults to true for malware, not allowed for tools + try { if (data.stix && data.stix.type === 'malware' && typeof data.stix.is_family !== 'boolean') { data.stix.is_family = true; } @@ -80,6 +80,6 @@ class SoftwareService extends BaseService { throw err; } } - +} module.exports = new SoftwareService(null, SoftwareRepository); \ No newline at end of file From d72176705b8cf31530bb0b6576f5035cfe9a8f94 Mon Sep 17 00:00:00 2001 From: Jack Sheriff <> Date: Wed, 24 Jan 2024 17:33:10 -0500 Subject: [PATCH 26/26] Minor cleanup and formatting. --- app/controllers/software-controller.js | 6 +- app/exceptions/index.js | 8 -- app/repository/software-repository.js | 4 +- app/services/_base.service.js | 2 - app/services/software-service.js | 121 +++++++++++++------------ 5 files changed, 64 insertions(+), 77 deletions(-) diff --git a/app/controllers/software-controller.js b/app/controllers/software-controller.js index 010b6ce4..da22f886 100644 --- a/app/controllers/software-controller.js +++ b/app/controllers/software-controller.js @@ -2,7 +2,7 @@ const softwareService = require('../services/software-service'); const logger = require('../lib/logger'); -const { DuplicateIdError, BadlyFormattedParameterError, InvalidQueryStringParameterError, MissingPropertyError, PropertyNotAllowedError } = require('../exceptions'); +const { DuplicateIdError, BadlyFormattedParameterError, InvalidQueryStringParameterError, PropertyNotAllowedError } = require('../exceptions'); exports.retrieveAll = async function(req, res) { const options = { @@ -113,10 +113,6 @@ exports.create = async function(req, res) { logger.warn("Duplicate stix.id and stix.modified"); return res.status(409).send('Unable to create software. Duplicate stix.id and stix.modified properties.'); } - else if (err instanceof MissingPropertyError) { - logger.warn(`Unable to create software, missing property ${ err.propertyName }`); - return res.status(400).send(`Unable to create software, missing property ${ err.propertyName }`); - } else if (err instanceof PropertyNotAllowedError) { logger.warn(`Unable to create software, property ${ err.propertyName } is not allowed`); return res.status(400).send(`Unable to create software, property ${ err.propertyName } is not allowed`); diff --git a/app/exceptions/index.js b/app/exceptions/index.js index 4a5ecc99..13476470 100644 --- a/app/exceptions/index.js +++ b/app/exceptions/index.js @@ -87,12 +87,6 @@ class NotImplementedError extends CustomError { } } -class MissingPropertyError extends CustomError { - constructor(propertyName, options) { - super(`Unable to create software, missing property ${propertyName}`, options); - } -} - class PropertyNotAllowedError extends CustomError { constructor(propertyName, options) { super(`Unable to create software, property ${propertyName} is not allowed`, options); @@ -126,8 +120,6 @@ module.exports = { IdentityServiceError, TechniquesServiceError, TacticsServiceError, - - MissingPropertyError, PropertyNotAllowedError, InvalidTypeError, diff --git a/app/repository/software-repository.js b/app/repository/software-repository.js index 80fe0680..a1aeb200 100644 --- a/app/repository/software-repository.js +++ b/app/repository/software-repository.js @@ -3,8 +3,6 @@ const BaseRepository = require('./_base.repository'); const Software = require('../models/software-model'); -class SoftwareRepository extends BaseRepository { - -} +class SoftwareRepository extends BaseRepository { } module.exports = new SoftwareRepository(Software); \ No newline at end of file diff --git a/app/services/_base.service.js b/app/services/_base.service.js index 521445f1..0b3e1069 100644 --- a/app/services/_base.service.js +++ b/app/services/_base.service.js @@ -238,11 +238,9 @@ class BaseService extends AbstractService { callback = arguments[arguments.length - 1]; } - if (data?.stix?.type !== this.type) { throw new InvalidTypeError(); } - // eslint-disable-next-line no-useless-catch try { diff --git a/app/services/software-service.js b/app/services/software-service.js index 3019a996..18899a6e 100644 --- a/app/services/software-service.js +++ b/app/services/software-service.js @@ -5,81 +5,84 @@ const systemConfigurationService = require('./system-configuration-service'); const attackObjectsService = require('./attack-objects-service'); const config = require('../config/config'); -const { PropertyNotAllowedError} = require('../exceptions'); +const { PropertyNotAllowedError, InvalidTypeError } = require('../exceptions'); const BaseService = require('./_base.service'); -const SoftwareRepository = require('../repository/software-repository'); +const softwareRepository = require('../repository/software-repository'); class SoftwareService extends BaseService { - async create(data, options, callback) { // This function handles two use cases: // 1. This is a completely new object. Create a new object and generate the stix.id if not already // provided. Set both stix.created_by_ref and stix.x_mitre_modified_by_ref to the organization identity. // 2. This is a new version of an existing object. Create a new object with the specified id. // Set stix.x_mitre_modified_by_ref to the organization identity. - + // is_family defaults to true for malware, not allowed for tools - try { - if (data.stix && data.stix.type === 'malware' && typeof data.stix.is_family !== 'boolean') { - data.stix.is_family = true; - } - else if (data.stix && data.stix.type === 'tool' && data.stix.is_family !== undefined) { - throw new PropertyNotAllowedError; - } + try { + if (data?.stix?.type !== 'malware' && data?.stix?.type !== 'tool') { + throw new InvalidTypeError(); + } - options = options || {}; - if (!options.import) { - // Set the ATT&CK Spec Version - data.stix.x_mitre_attack_spec_version = data.stix.x_mitre_attack_spec_version ?? config.app.attackSpecVersion; - - // Record the user account that created the object - if (options.userAccountId) { - data.workspace.workflow.created_by_user_account = options.userAccountId; + if (data.stix && data.stix.type === 'malware' && typeof data.stix.is_family !== 'boolean') { + data.stix.is_family = true; + } + else if (data.stix && data.stix.type === 'tool' && data.stix.is_family !== undefined) { + throw new PropertyNotAllowedError(); } - // Set the default marking definitions - await attackObjectsService.setDefaultMarkingDefinitions(data); + options = options || {}; + if (!options.import) { + // Set the ATT&CK Spec Version + data.stix.x_mitre_attack_spec_version = data.stix.x_mitre_attack_spec_version ?? config.app.attackSpecVersion; - // Get the organization identity - const organizationIdentityRef = await systemConfigurationService.retrieveOrganizationIdentityRef(); - - // Check for an existing object - let existingObject; - if (data.stix.id) { - existingObject = await this.repository.retrieveOneById(data.stix.id); - } + // Record the user account that created the object + if (options.userAccountId) { + data.workspace.workflow.created_by_user_account = options.userAccountId; + } - if (existingObject) { - // New version of an existing object - // Only set the x_mitre_modified_by_ref property - data.stix.x_mitre_modified_by_ref = organizationIdentityRef; - } - else { - // New object - // Assign a new STIX id if not already provided - if (!data.stix.id) { - // const stixIdPrefix = getStixIdPrefixFromModel(this.model.modelName, data.stix.type); - data.stix.id = `${data.stix.type}--${uuid.v4()}`; - } + // Set the default marking definitions + await attackObjectsService.setDefaultMarkingDefinitions(data); - // Set the created_by_ref and x_mitre_modified_by_ref properties - data.stix.created_by_ref = organizationIdentityRef; - data.stix.x_mitre_modified_by_ref = organizationIdentityRef; - } - } - const res = await this.repository.save(data); - if (callback) { - return callback(null, res); - } - return res; - } catch (err) { - if (callback) { - return callback(err); - } - throw err; - } -} + // Get the organization identity + const organizationIdentityRef = await systemConfigurationService.retrieveOrganizationIdentityRef(); + + // Check for an existing object + let existingObject; + if (data.stix.id) { + existingObject = await this.repository.retrieveOneById(data.stix.id); + } + + if (existingObject) { + // New version of an existing object + // Only set the x_mitre_modified_by_ref property + data.stix.x_mitre_modified_by_ref = organizationIdentityRef; + } + else { + // New object + // Assign a new STIX id if not already provided + if (!data.stix.id) { + // const stixIdPrefix = getStixIdPrefixFromModel(this.model.modelName, data.stix.type); + data.stix.id = `${data.stix.type}--${uuid.v4()}`; + } + + // Set the created_by_ref and x_mitre_modified_by_ref properties + data.stix.created_by_ref = organizationIdentityRef; + data.stix.x_mitre_modified_by_ref = organizationIdentityRef; + } + } + const res = await this.repository.save(data); + if (callback) { + return callback(null, res); + } + return res; + } catch (err) { + if (callback) { + return callback(err); + } + throw err; + } + } } -module.exports = new SoftwareService(null, SoftwareRepository); \ No newline at end of file +module.exports = new SoftwareService(null, softwareRepository); \ No newline at end of file