UNPKG

@mdf.js/openc2-core

Version:

MMS - API Core - OpenC2

153 lines 5.67 kB
"use strict"; /** * Copyright 2024 Mytra Control S.L. All rights reserved. * * Use of this source code is governed by an MIT-style license that can be found in the LICENSE file * or at https://opensource.org/licenses/MIT. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Registry = void 0; const core_1 = require("@mdf.js/core"); const crash_1 = require("@mdf.js/crash"); const stream_1 = require("stream"); const uuid_1 = require("uuid"); const helpers_1 = require("../../helpers"); class Registry extends stream_1.EventEmitter { /** * Creates a new Register instance * @param name - Component name * @param maxInactivityTime - Max time in minutes that a job could be pending state * @param registerLimit - Maximum number of entries in the message register */ constructor(name, maxInactivityTime = helpers_1.Constants.DEFAULT_MAX_INACTIVITY_TIME, registerLimit = helpers_1.Constants.DEFAULT_MESSAGES_REGISTERS_LIMIT) { super(); this.name = name; this.maxInactivityTime = maxInactivityTime; this.registerLimit = registerLimit; /** Component identification */ this.componentId = (0, uuid_1.v4)(); /** Array of messages used as fifo register */ this.messages = []; /** Processed jobs */ this.executedJobs = []; /** Pending commands */ this.pendingJobs = new Map(); /** Represent the actual status of the register of jobs */ this._status = 'pass'; /** * Check if there are pending jobs that have been pending for too long */ this.checkOldPendingJobs = () => { const now = Date.now(); let newStatus = 'pass'; for (const [, job] of this.pendingJobs[Symbol.iterator]()) { if (now - job.createdAt.getTime() > 1000 * 60 * this.maxInactivityTime) { job.done(new crash_1.Crash(`Job cancelled after ${this.maxInactivityTime} minutes of inactivity`, this.componentId)); } else if (now - job.createdAt.getTime() > (1000 * 60 * this.maxInactivityTime) / 2) { newStatus = 'warn'; } } if (newStatus !== this._status) { this._status = newStatus; this.emit('status', this._status); } }; this.timeInterval = (Math.max(this.maxInactivityTime, helpers_1.Constants.DEFAULT_MIN_INACTIVITY_TIME) * 60 * 1000) / 2; } push(item) { if (item instanceof core_1.Jobs.JobHandler) { this.pendingJobs.set(item.uuid, item); } else { this.messages.push(item); if (this.messages.length > this.registerLimit) { this.messages.shift(); } } if (!this.interval && this.pendingJobs.size > 0) { this.interval = setInterval(this.checkOldPendingJobs, this.timeInterval); } } /** Perform the cleaning of all the resources */ clear() { this.messages.length = 0; this.executedJobs.length = 0; this.pendingJobs.clear(); if (this.interval) { clearInterval(this.interval); this.interval = undefined; } } /** * Return the job from the register and return it * @param uuid - Job uuid * @returns */ delete(uuid) { const job = this.pendingJobs.get(uuid); if (job) { this.executedJobs.push({ ...job.result(), command: job.data }); if (this.executedJobs.length > this.registerLimit) { this.executedJobs.shift(); } this.pendingJobs.delete(uuid); if (this.pendingJobs.size === 0) { clearInterval(this.interval); this.interval = undefined; } return job; } else { return undefined; } } /** Return a resume of the pending jobs in the register */ resume() { const pendingJobsResume = []; for (const job of this.pendingJobs.values()) { for (const target of Object.keys(job.data.content.target)) { pendingJobsResume.push(`${job.createdAt.toISOString()} - ${job.data.content.action}:${target} - ${job.status}`); } } return pendingJobsResume; } /** Return the status of the register */ get status() { return this._status; } /** * Return the status of the stream in a standard format * @returns _check object_ as defined in the draft standard * https://datatracker.ietf.org/doc/html/draft-inadarei-api-health-check-05 */ get checks() { const check = { status: this._status, componentId: this.componentId, componentType: 'source', observedValue: this.pendingJobs.size, observedUnit: 'pending commands', time: new Date().toISOString(), output: this._status !== 'pass' ? this.resume() : undefined, }; return { [`${this.name}:commands`]: [check], }; } /** Fake start method used to implement the Resource interface */ async start() { return Promise.resolve(); } /** Fake stop method used to implement the Resource interface */ async stop() { return Promise.resolve(); } /** Fake close method used to implement the Resource interface */ async close() { return Promise.resolve(); } } exports.Registry = Registry; //# sourceMappingURL=Registry.js.map