Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@enterprise-cmcs/macpro-security-hub-sync

Package Overview
Dependencies
Maintainers
4
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@enterprise-cmcs/macpro-security-hub-sync - npm Package Compare versions

Comparing version 1.0.2 to 1.1.0

3

dist/libs/jira-lib.d.ts

@@ -5,8 +5,7 @@ import { IssueObject } from "jira-client";

jiraClosedStatuses: string[];
project: string;
constructor();
private static checkEnvVars;
getAllSecurityHubIssuesInJiraProject(): Promise<IssueObject[]>;
getAllSecurityHubIssuesInJiraProject(identifyingLabels: string[]): Promise<IssueObject[]>;
createNewIssue(issue: IssueObject): Promise<IssueObject>;
closeIssue(issueKey: string): Promise<void>;
}

@@ -36,6 +36,4 @@ "use strict";

jiraClosedStatuses;
project;
constructor() {
Jira.checkEnvVars();
this.project = process.env.PROJECT ?? "";
this.jiraClosedStatuses = process.env.JIRA_CLOSED_STATUSES

@@ -60,3 +58,2 @@ ? process.env.JIRA_CLOSED_STATUSES.split(",")

"JIRA_PROJECT",
"PROJECT",
];

@@ -68,5 +65,6 @@ const missingEnvVars = requiredEnvVars.filter((envVar) => !process.env[envVar]);

}
async getAllSecurityHubIssuesInJiraProject() {
async getAllSecurityHubIssuesInJiraProject(identifyingLabels) {
const labelQuery = identifyingLabels.reduce((accumulator, currentValue) => accumulator + `AND labels = ${currentValue} `, "");
const searchOptions = {};
const query = `project = ${process.env.JIRA_PROJECT} AND labels = ${this.project} AND labels = security-hub AND status not in ("${this.jiraClosedStatuses.join('","')}")`;
const query = `project = ${process.env.JIRA_PROJECT} AND labels = security-hub ${labelQuery} AND status not in ("${this.jiraClosedStatuses.join('","')}")`;
let totalIssuesReceived = 0;

@@ -76,2 +74,9 @@ let allIssues = [];

do {
// We want to do everything possible to prevent matching tickets that we shouldn't
if (!query.includes("AND labels = security-hub ")) {
throw "ERROR: Your query does not include the 'security-hub' label, and is too broad. Refusing to continue";
}
if (!query.match(" AND labels = [0-9]{12}")) {
throw "ERROR: Your query does not include an AWS Account ID as a label, and is too broad. Refusing to continue";
}
results = await this.jira.searchJira(query, searchOptions);

@@ -88,4 +93,2 @@ allIssues = allIssues.concat(results.issues);

issue.fields.project = { key: process.env.JIRA_PROJECT };
// add aditional labels
issue.fields.labels.push(this.project);
const response = await this.jira.addNewIssue(issue);

@@ -107,3 +110,2 @@ response["webUrl"] = `https://${process.env.JIRA_HOST}/browse/${response.key}`;

const doneTransition = transitions.transitions.find((t) => t.name === "Done");
const doneTransitionId = doneTransition ? doneTransition.id : undefined;
if (!doneTransition) {

@@ -110,0 +112,0 @@ console.error(`Cannot find "Done" transition for issue ${issueKey}`);

@@ -6,2 +6,5 @@ import { SecurityHubFinding } from "./libs";

severities?: string[];
customJiraFields?: {
[id: string]: any;
};
}

@@ -11,10 +14,13 @@ export declare class SecurityHubJiraSync {

private readonly securityHub;
private readonly customJiraFields;
private readonly region;
constructor(options?: SecurityHubJiraSyncOptions);
sync(): Promise<void>;
closeIssuesForResolvedFindings(jiraIssues: IssueObject[], shFindings: SecurityHubFinding[]): void;
getAWSAccountID(): Promise<string>;
closeIssuesForResolvedFindings(jiraIssues: IssueObject[], shFindings: SecurityHubFinding[]): Promise<void>;
createIssueBody(finding: SecurityHubFinding): string;
createSecurityHubFindingUrl(standardsControlArn?: string): string;
createJiraIssueFromFinding(finding: SecurityHubFinding): Promise<void>;
createJiraIssuesForNewFindings(jiraIssues: IssueObject[], shFindings: SecurityHubFinding[]): void;
createJiraIssueFromFinding(finding: SecurityHubFinding, identifyingLabels: string[]): Promise<void>;
createJiraIssuesForNewFindings(jiraIssues: IssueObject[], shFindings: SecurityHubFinding[], identifyingLabels: string[]): void;
}
export {};

@@ -5,13 +5,21 @@ "use strict";

const libs_1 = require("./libs");
const client_sts_1 = require("@aws-sdk/client-sts");
class SecurityHubJiraSync {
jira;
securityHub;
customJiraFields;
region;
constructor(options = {}) {
const { region = "us-east-1", severities = ["HIGH", "CRITICAL"] } = options;
const { region = "us-east-1", severities = ["MEDIUM", "HIGH", "CRITICAL"], customJiraFields = {}, } = options;
this.securityHub = new libs_1.SecurityHub({ region, severities });
this.region = region;
this.jira = new libs_1.Jira();
this.customJiraFields = customJiraFields;
}
async sync() {
// Step 0. Gather and set some information that will be used throughout this function
const accountId = await this.getAWSAccountID();
const identifyingLabels = [accountId, this.region];
// Step 1. Get all open Security Hub issues from Jira
const jiraIssues = await this.jira.getAllSecurityHubIssuesInJiraProject();
const jiraIssues = await this.jira.getAllSecurityHubIssuesInJiraProject(identifyingLabels);
// console.log(

@@ -24,14 +32,26 @@ // "all current statuses on security hub issues:",

// Step 3. Close existing Jira issues if their finding is no longer active/current
this.closeIssuesForResolvedFindings(jiraIssues, shFindings);
await this.closeIssuesForResolvedFindings(jiraIssues, shFindings);
// Step 4. Create Jira issue for current findings that do not already have a Jira issue
this.createJiraIssuesForNewFindings(jiraIssues, shFindings);
await this.createJiraIssuesForNewFindings(jiraIssues, shFindings, identifyingLabels);
}
closeIssuesForResolvedFindings(jiraIssues, shFindings) {
async getAWSAccountID() {
const client = new client_sts_1.STSClient({
region: this.region,
});
const command = new client_sts_1.GetCallerIdentityCommand({});
const response = await client.send(command);
let accountID = response.Account || "";
if (!accountID.match("[0-9]{12}")) {
throw "ERROR: An issue was encountered when looking up your AWS Account ID. Refusing to continue.";
}
return accountID;
}
async closeIssuesForResolvedFindings(jiraIssues, shFindings) {
const expectedJiraIssueTitles = Array.from(new Set(shFindings.map((finding) => `SecurityHub Finding - ${finding.title}`)));
// close all security-hub labeled Jira issues that do not have an active finding
jiraIssues.forEach((issue) => {
if (!expectedJiraIssueTitles.includes(issue.fields.summary)) {
this.jira.closeIssue(issue.key);
for (var i = 0; i < jiraIssues.length; i++) {
if (!expectedJiraIssueTitles.includes(jiraIssues[i].fields.summary)) {
await this.jira.closeIssue(jiraIssues[i].key);
}
});
}
}

@@ -85,3 +105,3 @@ createIssueBody(finding) {

}
async createJiraIssueFromFinding(finding) {
async createJiraIssueFromFinding(finding, identifyingLabels) {
const newIssueData = {

@@ -94,7 +114,7 @@ fields: {

"security-hub",
finding.region,
finding.severity,
finding.accountAlias,
finding.awsAccountId,
...identifyingLabels,
],
...this.customJiraFields,
},

@@ -105,3 +125,3 @@ };

}
createJiraIssuesForNewFindings(jiraIssues, shFindings) {
createJiraIssuesForNewFindings(jiraIssues, shFindings, identifyingLabels) {
const existingJiraIssueTitles = jiraIssues.map((i) => i.fields.summary);

@@ -113,5 +133,5 @@ const uniqueSecurityHubFindings = [

.filter((finding) => !existingJiraIssueTitles.includes(`SecurityHub Finding - ${finding.title}`))
.forEach((finding) => this.createJiraIssueFromFinding(finding));
.forEach((finding) => this.createJiraIssueFromFinding(finding, identifyingLabels));
}
}
exports.SecurityHubJiraSync = SecurityHubJiraSync;

@@ -7,3 +7,3 @@ {

},
"version": "1.0.2",
"version": "1.1.0",
"description": "NPM module to create Jira issues for all findings in Security Hub for the current AWS account..",

@@ -31,7 +31,4 @@ "main": "./dist/index.js",

"devDependencies": {
"@aws-sdk/client-iam": "^3.259.0",
"@aws-sdk/client-securityhub": "^3.257.0",
"@semantic-release/changelog": "^6.0.2",
"@semantic-release/git": "^10.0.1",
"@types/jira-client": "^7.1.6",
"@types/node": "^18.11.18",

@@ -45,3 +42,3 @@ "@vitest/coverage-c8": "^0.28.2",

"typescript": "^4.9.4",
"vitest": "^0.28.2"
"vitest": "^0.28.4"
},

@@ -60,2 +57,6 @@ "release": {

"dependencies": {
"@aws-sdk/client-iam": "^3.266.0",
"@aws-sdk/client-securityhub": "^3.266.0",
"@aws-sdk/client-sts": "^3.266.1",
"@types/jira-client": "^7.1.6",
"dotenv": "^16.0.3",

@@ -62,0 +63,0 @@ "jira-client": "^8.2.2"

<h1 align="center" style="border-bottom: none;">macpro-security-hub-sync</h1>
<h3 align="center">NPM module to create Jira issues for all findings in Security Hub for the current AWS account.</h3>
<p align="center">
<a href="https://cmsgov.slack.com/archives/C04MBTV136X">
<img alt="Slack" src="https://img.shields.io/badge/Slack-channel-purple.svg">
</a>
<a href="https://github.com/Enterprise-CMCS/macpro-security-hub-sync/releases/latest">

@@ -27,50 +30,60 @@ <img alt="latest release" src="https://img.shields.io/github/release/Enterprise-CMCS/macpro-security-hub-sync.svg">

## Information
## Usage
This package syncs AWS Security Hub Findings to Jira.
Set a few enviroment variables that are expected by the package:
- When the sync utility is run, each Security Hub Finding type (Title) is represented as a single issue. So if you have violated the 'S3.8' rule three individual times, you will have one S3.8 Jira Issue created.
- By default, CRITICAL and HIGH severity findings get issues created in Jira. However, this is configurable in either direction (more or less sensitivity).
- When the utility runs, previously created Jira issues that no longer have an active finding are closed. In this way, Jira issues can be automatically closed as the Findings are resolved, if you run the utility on a schedule (recommended).
## Synchronization Process
The SecurityHubJiraSyncOptions class's main function is sync. The sync process follows this process:
Step 1. Get all open Security Hub issues from Jira
Step 2. Get all current findings from Security Hub
Step 3. Close existing Jira issues if their finding is no longer active/current
Step 4. Create Jira issue for current findings that do not already have a Jira issue
## Usage and Getting Started
To install the package run the following command:
```
npm install --save-dev @enterprise-cmcs/macpro-security-hub-sync
export JIRA_HOST=yourorg.atlassian.net
export JIRA_PROJECT=OY2 // This is the ID for the Jira Project you want to interact with
export JIRA_USERNAME="myuser@example.com"
export JIRA_TOKEN="a very long string" // This should be a [Personal Access Token](https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html) that you generate
```
or
Install the package with a dependency manager of your choice, probably as a dev dependency:
```
yarn add --dev @enterprise-cmcs/macpro-security-hub-sync
npm install @enterprise-cmcs/macpro-security-hub-sync --save-dev
```
After installing the package in your project include this import statement
Import the package and execute a sync:
```
import { SecurityHubJiraSync } from "@enterprise-cmcs/macpro-security-hub-sync";
await new SecurityHubJiraSync().sync();
```
With SecurityHubJiraSync imported you can now execute it like:
Or, override defaults by passing more options:
```
await new SecurityHubJiraSync({ region = "us-east-1", severities: ["MEDIUM"] }).sync();
await new SecurityHubJiraSync({
region: "us-west-2", // Which regional Security Hub to scrape; default is "us-east-1"
severities: ["HIGH","CRITICAL"], // List of all severities to find; default is ["MEDIUM","HIGH","CRITICAL"]
customJiraFields: { // A map of custom fields to add to each Jira Issue; no default; making this nicer is WIP
customfield_14117: [{value: "Platform Team"}],
customfield_14151: [{value: "Not Applicable "}],
}
}).sync();
```
## Contributing
## Info
Found a bug, want to help with updating the docs or maybe you want to help add a feature. Refer to our contribution documentation for more information: [Documentation](./docs/CONTRIBUTING.MD)
#### Overview
## Instructions to test locally with a yarn project
This package syncs AWS Security Hub Findings to Jira.
- When the sync utility is run, each Security Hub Finding type (Title) is represented as a single issue. So if you have violated the 'S3.8' rule three individual times, you will have one S3.8 Jira Issue created.
- By default, CRITICAL and HIGH severity findings get issues created in Jira. However, this is configurable in either direction (more or less sensitivity).
- When the utility runs, previously created Jira issues that no longer have an active finding are closed. In this way, Jira issues can be automatically closed as the Findings are resolved, if you run the utility on a schedule (recommended).
#### Sync Process
The SecurityHubJiraSyncOptions class's main function is sync. The sync process follows this process:
1. Get all open Security Hub issues (identified by a label convention) from Jira
2. Get all current findings from Security Hub
3. Close existing Jira issues if their finding is no longer active/current
4. Create Jira issue (including labels from our label convention) for current findings that do not already have a Jira issue
#### Instructions to test locally with a yarn project
- in your terminal from your local clone of macpro-security-hub-sync with your development branch

@@ -107,6 +120,12 @@ - `yarn link` (note, when testing is complete, run `yarn unlink`)

## Contributing
You can check out our current open issues [here](https://github.com/Enterprise-CMCS/macpro-security-hub-sync/issues). Please feel free to open new issues for bugs or enhancements.
Also, join us on [Slack](https://cmsgov.slack.com/archives/C04MBTV136X)
## License
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![License](https://img.shields.io/badge/License-CC0--1.0--Universal-blue.svg)](https://creativecommons.org/publicdomain/zero/1.0/legalcode)
See [LICENSE](LICENSE) for full details.

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc