Skip to content

Commit

Permalink
Merge pull request #145 from NSWC-Crane/CHRIS_DEV
Browse files Browse the repository at this point in the history
1.0.0?
  • Loading branch information
crodriguez6497 authored Jan 24, 2025
2 parents 932de25 + 5107d00 commit e7f100c
Show file tree
Hide file tree
Showing 145 changed files with 12,817 additions and 9,492 deletions.
5 changes: 4 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ RUN npm ci

COPY --chown=node:node api/. .

COPY --chown=node:node --from=build /app/client/dist ../client/dist
COPY --chown=node:node --from=build /app/client/dist/browser ../client/dist/browser

RUN mkdir docs
COPY --chown=node:node ./docs/_build/html ../docs

USER root
RUN \
Expand Down
19 changes: 18 additions & 1 deletion api/Controllers/Operation.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

const operationService = require('../Services/operationService');
const config = require('../utils/config');
const SmError = require('../utils/error.js');

module.exports.getConfiguration = async function getConfiguration(req, res, next) {
try {
Expand Down Expand Up @@ -51,4 +52,20 @@ module.exports.deleteConfigurationItem = async function deleteConfigurationItem(
} catch (err) {
next(err);
}
};
};

module.exports.getAppInfo = async function getAppInfo(req, res, next) {
try {
let elevate = req.query.elevate
if (elevate) {
const response = await operationService.getAppInfo();
res.json(response);
}
else {
throw new SmError.PrivilegeError();
}
}
catch (err) {
next(err);
}
}
13 changes: 13 additions & 0 deletions api/Controllers/Poam.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,19 @@ module.exports.getPluginIDsWithPoam = async function getPluginIDsWithPoam(req, r
}
};

module.exports.getPluginIDsWithPoamByCollection = async function getPluginIDsWithPoamByCollection(req, res, next) {
try {
const pluginIDs = await poamService.getPluginIDsWithPoamByCollection(req, res, next);
res.status(200).json(pluginIDs);
} catch (error) {
if (error.status === 400) {
res.status(400).json({ error: 'Validation Error', detail: error.errors });
} else {
res.status(500).json({ error: 'Internal Server Error', detail: error.message });
}
}
};

module.exports.postPoam = async function postPoam(req, res, next) {
try {
const poam = await poamService.postPoam(req, res, next);
Expand Down
260 changes: 259 additions & 1 deletion api/Services/operationService.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
const config = require('../utils/config')
const dbUtils = require('./utils')
const mysql = require('mysql2')
const klona = require('../utils/klona');
const logger = require('../utils/logger');
const os = require('node:os');

async function withConnection(callback) {
const connection = await dbUtils.pool.getConnection();
Expand Down Expand Up @@ -62,4 +65,259 @@ exports.deleteConfigurationItem = async function (key) {
} catch (err) {
throw { status: 500, message: err.message, stack: err.stack };
}
};
};

exports.getAppInfo = async function () {
const schema = 'cpat-appinfo-v0.9'
const sqlUserInfo = `
select
ud.userId,
ud.username,
ud.created,
ud.lastAccess,
coalesce(
JSON_EXTRACT(ud.lastClaims, "$.${config.oauth.claims.privilegesPath}"),
json_array()
) as privileges,
json_object(
"Viewer", sum(case when cg.accessLevel = 1 then 1 else 0 end),
"Submitter", sum(case when cg.accessLevel = 2 then 1 else 0 end),
"Approver", sum(case when cg.accessLevel = 3 then 1 else 0 end),
"CAT-I Approver", sum(case when cg.accessLevel = 4 then 1 else 0 end)
) as roles
from
user ud
left join collectionpermissions cg using (userId)
group by
ud.userId
`
const sqlInfoSchema = `
SELECT
TABLE_NAME as tableName,
TABLE_ROWS as tableRows,
TABLE_COLLATION as tableCollation,
AVG_ROW_LENGTH as avgRowLength,
DATA_LENGTH as dataLength,
INDEX_LENGTH as indexLength,
AUTO_INCREMENT as autoIncrement,
CREATE_TIME as createTime,
UPDATE_TIME as updateTime
FROM
information_schema.TABLES
WHERE
TABLE_SCHEMA = ?
and TABLE_TYPE='BASE TABLE'
ORDER BY
TABLE_NAME`
const sqlMySqlVersion = `SELECT VERSION() as version`

const mySqlVariablesOnly = [
'innodb_buffer_pool_size',
'innodb_log_buffer_size',
'innodb_log_file_size',
'tmp_table_size',
'key_buffer_size',
'max_heap_table_size',
'temptable_max_mmap',
'sort_buffer_size',
'read_buffer_size',
'read_rnd_buffer_size',
'join_buffer_size',
'binlog_cache_size',
'tmp_table_size',
'innodb_buffer_pool_instances',
'innodb_io_capacity',
'innodb_io_capacity_max',
'innodb_flush_sync',
'innodb_io_capacity_max',
'innodb_lock_wait_timeout',
'version',
'version_compile_machine',
'version_compile_os',
'long_query_time'
]
const sqlMySqlVariablesValues = `
SELECT
variable_name,
variable_value as value
FROM
performance_schema.global_variables
WHERE
variable_name IN (${mySqlVariablesOnly.map(v => `'${v}'`).join(',')})
ORDER by variable_name
`
const mySqlStatusOnly = [
'Bytes_received',
'Bytes_sent',
'Handler_commit',
'Handler_update',
'Handler_write',
'Innodb_buffer_pool_bytes_data',
'Innodb_row_lock_waits',
'Innodb_rows_read',
'Innodb_rows_updated',
'Innodb_rows_inserted',
'Innodb_row_lock_time_avg',
'Innodb_row_lock_time_max',
'Created_tmp_files',
'Created_tmp_tables',
'Max_used_connections',
'Open_tables',
'Opened_tables',
'Queries',
'Select_full_join',
'Slow_queries',
'Table_locks_immediate',
'Table_locks_waited',
'Threads_created',
'Uptime'
]
const sqlMySqlStatusValues = `
SELECT
variable_name,
variable_value as value
FROM
performance_schema.global_status
WHERE
variable_name IN (
${mySqlStatusOnly.map(v => `'${v}'`).join(',')}
)
ORDER by variable_name
`
const [schemaInfoArray] = await dbUtils.pool.query(sqlInfoSchema, [config.database.schema])
const tables = createObjectFromKeyValue(schemaInfoArray, "tableName")

const rowCountQueries = []
for (const table in tables) {
rowCountQueries.push(dbUtils.pool.query(`SELECT "${table}" as tableName, count(*) as rowCount from ${table}`))
}

let [
[userInfo],
[mySqlVersion],
[mySqlVariables],
[mySqlStatus],
rowCountResults
] = await Promise.all([
dbUtils.pool.query(sqlUserInfo),
dbUtils.pool.query(sqlMySqlVersion),
dbUtils.pool.query(sqlMySqlVariablesValues),
dbUtils.pool.query(sqlMySqlStatusValues),
Promise.all(rowCountQueries)
])

for (const result of rowCountResults) {
tables[result[0][0].tableName].rowCount = result[0][0].rowCount
}

const cpatPrivs = ['admin', 'cpat_write', 'user']
for (const user of userInfo) {
user.privileges = user.privileges.filter(v => cpatPrivs.includes(v))
}

const userPrivilegeCounts = breakOutPrivilegeUsage(userInfo)

const requests = klona(logger.requestStats);
requests.operationIds = sortObjectByKeys(requests.operationIds);


const returnObj = {
date: new Date().toISOString(),
schema,
version: config.version,
requests,
users: {
userInfo: createObjectFromKeyValue(userInfo, "userId", null),
userPrivilegeCounts
},
mysql: {
version: mySqlVersion[0].version,
tables,
variables: createObjectFromKeyValue(mySqlVariables, "variable_name", "value"),
status: createObjectFromKeyValue(mySqlStatus, "variable_name", "value")
},
nodejs: getNodeValues()
}
return returnObj

function createObjectFromKeyValue(data, keyPropertyName, valuePropertyName = null, includeKey = false) {
return data.reduce((acc, item) => {
const { [keyPropertyName]: key, ...rest } = item
acc[key] = valuePropertyName ? item[valuePropertyName] : includeKey ? item : rest
return acc
}, {})
}

function sortObjectByKeys(obj) {
const sortedObj = {}
for (const key of Object.keys(obj).sort()) {
sortedObj[key] = obj[key]
}
return sortedObj
}

function breakOutPrivilegeUsage(userInfo) {
let privilegeCounts = {
overall: { none: 0 },
activeInLast30Days: { none: 0 },
activeInLast90Days: { none: 0 }
}

const currentTime = Math.floor(Date.now() / 1000)
const thirtyDaysAgo = currentTime - (30 * 24 * 60 * 60)
const ninetyDaysAgo = currentTime - (90 * 24 * 60 * 60)
const updateCounts = (categoryCounts, userPrivs) => {
if (userPrivs.length === 0) {
categoryCounts.none++
}
for (const privilege of userPrivs) {
categoryCounts[privilege] = categoryCounts[privilege] ? categoryCounts[privilege] + 1 : 1
}
}

for (const user of userInfo) {
updateCounts(privilegeCounts.overall, user.privileges)
if (user.lastAccess >= ninetyDaysAgo) {
updateCounts(privilegeCounts.activeInLast90Days, user.privileges)
}
if (user.lastAccess >= thirtyDaysAgo) {
updateCounts(privilegeCounts.activeInLast30Days, user.privileges)
}
}
return privilegeCounts
}

function getNodeValues() {
const { environmentVariables, header, resourceUsage } = process.report.getReport()

const environment = {}
for (const [key, value] of Object.entries(environmentVariables)) {
if (/^(NODE|CPAT)_/.test(key)) {
environment[key] = key === 'CPAT_DB_PASSWORD' ? '***' : value
}
}
const { platform, arch, nodejsVersion, cpus, osMachine, osName, osRelease } = header
for (let x = 0; x < cpus.length; x++) {
cpus[x] = { model: cpus[x].model, speed: cpus[x].speed }
}
const loadAverage = os.loadavg().join(', ')

const memory = process.memoryUsage()
memory.maxRss = resourceUsage.maxRss
return {
version: nodejsVersion.substring(1),
uptime: process.uptime(),
os: {
platform,
arch,
osMachine,
osName,
osRelease,
loadAverage
},
environment,
memory,
cpus
}
}
}
26 changes: 26 additions & 0 deletions api/Services/poamService.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,32 @@ exports.getPluginIDsWithPoam = async function getPluginIDsWithPoam(req, res, nex
}
};

exports.getPluginIDsWithPoamByCollection = async function getPluginIDsWithPoamByCollection(req, res, next) {
try {
return await withConnection(async (connection) => {
let sql = `
SELECT poamId, status, vulnerabilityId
FROM cpat.poam
WHERE collectionId = ?
UNION ALL
SELECT p.poamId, 'Associated' as status, p.associatedVulnerability as vulnerabilityId
FROM cpat.poamassociatedvulnerabilities p
WHERE EXISTS (
SELECT 1
FROM cpat.poam po
WHERE po.vulnerabilityId = p.associatedVulnerability
AND po.collectionId = ?
)
ORDER BY poamId;
`;
let [PluginIDs] = await connection.query(sql, [req.params.collectionId, req.params.collectionId]);
return PluginIDs;
});
} catch (error) {
return { error: error.message };
}
};

exports.postPoam = async function postPoam(req) {
const requiredFields = ['collectionId', 'vulnerabilitySource', 'aaPackage', 'rawSeverity', 'submitterId'];
for (let field of requiredFields) {
Expand Down
2 changes: 1 addition & 1 deletion api/example_env.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ CPAT_CLASSIFICATION="U"
#client config
CPAT_API_BASE="api"
CPAT_CLIENT_DISABLED="false"
CPAT_CLIENT_DIRECTORY="../client/dist"
CPAT_CLIENT_DIRECTORY="../client/dist/browser"
CPAT_CLIENT_REFRESH_DISABLED="false"

#docs config
Expand Down
4 changes: 3 additions & 1 deletion api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,9 @@ const CPAT = {
extraScopes: "${config.stigman.extraScopes ?? ''}",
},
features: {
marketplaceDisabled: ${config.client.features.marketplaceDisabled}
marketplaceDisabled: ${config.client.features.marketplaceDisabled},
docsDisabled: ${config.docs.disabled},
swaggerUiEnabled: ${config.swaggerUi.enabled}
}
}
}
Expand Down
Loading

0 comments on commit e7f100c

Please sign in to comment.