Skip to content

Commit

Permalink
Merge pull request #259 from center-for-threat-informed-defense/featu…
Browse files Browse the repository at this point in the history
…re/WB-27

Feature/wb 27
  • Loading branch information
ElJocko authored May 31, 2023
2 parents d421247 + d973f8d commit 3878fa2
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 2 deletions.
3 changes: 3 additions & 0 deletions app/api/definitions/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ paths:
/api/matrices/{stixId}/modified/{modified}:
$ref: 'paths/matrices-paths.yml#/paths/~1api~1matrices~1{stixId}~1modified~1{modified}'

/api/matrices/{stixId}/modified/{modified}/techniques:
$ref: 'paths/matrices-paths.yml#/paths/~1api~1matrices~1{stixId}~1modified~1{modified}~1techniques'

# Identities
/api/identities:
$ref: 'paths/identities-paths.yml#/paths/~1api~1identities'
Expand Down
28 changes: 27 additions & 1 deletion app/api/definitions/paths/matrices-paths.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ paths:
type: array
items:
$ref: '../components/matrices.yml#/components/schemas/matrix'

post:
summary: 'Create a matrix'
operationId: 'matrix-create'
Expand Down Expand Up @@ -269,3 +268,30 @@ paths:
description: 'The matrix was successfully deleted.'
'404':
description: 'A matrix with the requested STIX id and modified date was not found.'

/api/matrices/{stixId}/modified/{modified}/techniques:
get:
summary: 'Retrieves techniques and subtechniques for the supplied matrix'
operationId: 'techniques-get-by-id-and-modified'
description: |
This endpoint retrieves all the techniques and subtechniques found in the supplied matrix.
tags:
- 'Matrices'
parameters:
- name: stixId
in: path
description: 'STIX id of the matrix to retrieve techniques and subtechniques'
required: true
schema:
type: string
- name: modified
in: path
description: 'modified date of the matrix to retrieve'
required: true
schema:
type: string
responses:
'200':
description: 'The techniques and subtechniques of a matrix matching the STIX id and modified date.'
'404':
description: 'A matrix with the requested STIX id and modified date was not found.'
23 changes: 23 additions & 0 deletions app/controllers/matrices-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,26 @@ exports.deleteById = function(req, res) {
}
});
};

exports.retrieveTechniquesForMatrix = function(req, res) {
matricesService.retrieveTechniquesForMatrix(req.params.stixId, req.params.modified, function (err, techniquesByTactic) {
if (err) {
if (err.message === matricesService.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 techniques for matrix. Server error.');
}
} else {
if (!techniquesByTactic) {
return res.status(404).send('Matrix not found.');
}
else {
logger.debug(`Success: Retrieved techniques for matrix with id ${ req.params.stixId }`);
return res.status(200).send(techniquesByTactic);
}
}
});
};
7 changes: 7 additions & 0 deletions app/routes/matrices-routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,11 @@ router.route('/matrices/:stixId/modified/:modified')
matricesController.deleteVersionById
);

router.route('/matrices/:stixId/modified/:modified/techniques')
.get(
authn.authenticate,
authz.requireRole(authz.visitorOrHigher, authz.readOnlyService),
matricesController.retrieveTechniquesForMatrix
);

module.exports = router;
82 changes: 81 additions & 1 deletion app/services/matrices-service.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

const uuid = require('uuid');
const util = require('util');
const Matrix = require('../models/matrix-model');
const systemConfigurationService = require('./system-configuration-service');
const identitiesService = require('./identities-service');
Expand Down Expand Up @@ -213,6 +214,86 @@ exports.retrieveVersionById = function(stixId, modified, callback) {
});
};

let retrieveTacticById;
let retrieveTechniquesForTactic;
exports.retrieveTechniquesForMatrix = function(stixId, modified, callback) {
// Retrieve the versions of the matrix techniques with the matching stixId and modified date

// Late binding to avoid circular dependency between modules
if (!retrieveTacticById) {
const tacticsService = require('./tactics-service');
retrieveTacticById = util.promisify(tacticsService.retrieveById);
}
if (!retrieveTechniquesForTactic) {
const tacticsService = require('./tactics-service');
retrieveTechniquesForTactic = tacticsService.retrieveTechniquesForTactic;
}

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);
}

Matrix.findOne({ 'stix.id': stixId, 'stix.modified': modified }, async function(err, matrix) {
if (err) {
if (err.name === 'CastError') {
const error = new Error(errors.badlyFormattedParameter);
error.parameterName = 'stixId';
return callback(error);
}
else {
return callback(err);
}
}
else {
if (matrix) {
// get tactics, then query for techniques and sub-techniques
const options = { versions: 'latest', offset: 0, limit: 0 };
const tacticsTechniques = {};
for (const tacticId of matrix.stix.tactic_refs) {
const tactics = await retrieveTacticById(tacticId, options);
if (tactics.length) {
const tactic = tactics[0];
const techniques = await retrieveTechniquesForTactic(tacticId, tactic.stix.modified, options);
// Organize sub-techniques under parent techniques
const parentTechniques = [];
const subtechniques = [];
for (const technique of techniques) {
if (!technique.stix.x_mitre_is_subtechnique) {
parentTechniques.push(technique);
}
else {
subtechniques.push(technique);
}
}
for (const parentTechnique of parentTechniques) {
parentTechnique.subtechniques = [];
for (const subtechnique of subtechniques) {
if (subtechnique.workspace.attack_id.split(".")[0] === parentTechnique.workspace.attack_id) {
parentTechnique.subtechniques.push(subtechnique);
}
}
}
// Add techniques to tactic & store tactic
tactic.techniques = parentTechniques;
tacticsTechniques[tactic.stix.name] = tactic;
}
}
return callback(null, tacticsTechniques);
}
else {
return callback();
}
}
});
};

exports.createIsAsync = true;
exports.create = async function(data, options) {
// This function handles two use cases:
Expand Down Expand Up @@ -368,4 +449,3 @@ exports.deleteById = function (stixId, callback) {
}
});
};

0 comments on commit 3878fa2

Please sign in to comment.