├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── NOTICE.md
├── README.md
├── asset
├── architecture.png
├── issue-workflow.xml
├── jira-cloud-status.png
├── jira-cloud-transitions.png
└── workflow.png
├── conf
└── params_prod.sh
├── deploy.sh
├── src
├── code
│ ├── config
│ │ └── config.json
│ ├── security_hub_integration.py
│ ├── sync_securityhub.py
│ ├── test_securityhub_integration.py
│ └── utils.py
└── template
│ ├── cloudformation_template.yaml
│ └── lpt-basic.yaml
├── test.sh
└── test
├── custom_new.template
├── custom_notified_existing.template
├── imported_archived_existing.template
├── imported_archived_new.template
├── imported_archived_notified.template
├── imported_new_automated_standard_check.template
└── imported_new_notautomated.template
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .DS_Store
3 | **/dist
4 | *.drawio
5 | *.vsdx
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | # Change Log
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](http://keepachangelog.com/)
6 | and this project adheres to [Semantic Versioning](http://semv
7 |
8 | ## [1.0] - 2022-03-14
9 |
10 | Initial release
11 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *main* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software is furnished to do so.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15 |
16 |
--------------------------------------------------------------------------------
/NOTICE.md:
--------------------------------------------------------------------------------
1 | jira
2 | 3.1.1
3 | BSD License
4 | Copyright (c) 2012, Atlassian Pty Ltd.
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
8 | following conditions are met:
9 |
10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following
11 | disclaimer.
12 |
13 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
14 | disclaimer in the documentation and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
17 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # aws-securityhub-jira-software-integration
3 |
4 | This solution supports a bidirectional integration between AWS Security Hub and Jira. Using this solution, you can automatically and manually create and update JIRA tickets from Security Hub findings. Security teams can use this integration to notify developer teams of severe security findings that require action.
5 |
6 | The solution allows you to:
7 |
8 | - Select which Security Hub controls automatically create or update tickets in Jira.
9 | - In the Security Hub console, use Security Hub custom actions to manually escalate tickets in Jira.
10 | - Automatically assign tickets in Jira based on the AWS account tags defined in AWS Organizations. If this tag is not defined, a default assignee is used.
11 | - Automatically suppress Security Hub findings that are marked as false positive or accepted risk in Jira.
12 | - Automatically close a Jira ticket when its related finding is archived in Security Hub.
13 | - Reopen Jira tickets when Security Hub findings reoccur.
14 |
15 | For the architecture diagram, prerequisites, and instructions for using this AWS Prescriptive Guidance pattern, see [Bidirectionally integrate AWS Security Hub with Jira software](https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/bidirectionally-integrate-aws-security-hub-with-jira-software.html).
16 |
--------------------------------------------------------------------------------
/asset/architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-securityhub-jira-software-integration/65898ab05357015fb39718d7f78aae2d38303368/asset/architecture.png
--------------------------------------------------------------------------------
/asset/issue-workflow.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JIRAUSER10000
6 | 1638056261643
7 |
8 |
9 |
10 |
11 | Create Issue
12 | com.atlassian.jira.workflow.validator.PermissionValidator
13 |
14 |
15 |
16 |
17 |
18 |
19 | com.atlassian.jira.workflow.function.issue.IssueCreateFunction
20 |
21 |
22 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
23 |
24 |
25 | 1
26 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | 1
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
45 |
46 |
47 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
48 |
49 |
50 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
51 |
52 |
53 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
54 |
55 |
56 | 13
57 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
71 |
72 |
73 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
74 |
75 |
76 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
77 |
78 |
79 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
80 |
81 |
82 | 13
83 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
97 |
98 |
99 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
100 |
101 |
102 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
103 |
104 |
105 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
106 |
107 |
108 | 13
109 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | 10002
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
128 |
129 |
130 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
131 |
132 |
133 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
134 |
135 |
136 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
137 |
138 |
139 | 13
140 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
154 |
155 |
156 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
157 |
158 |
159 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
160 |
161 |
162 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
163 |
164 |
165 | 13
166 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
180 |
181 |
182 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
183 |
184 |
185 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
186 |
187 |
188 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
189 |
190 |
191 | 13
192 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 | 10003
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
211 |
212 |
213 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
214 |
215 |
216 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
217 |
218 |
219 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
220 |
221 |
222 | 13
223 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
237 |
238 |
239 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
240 |
241 |
242 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
243 |
244 |
245 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
246 |
247 |
248 | 13
249 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 | 10004
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
268 |
269 |
270 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
271 |
272 |
273 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
274 |
275 |
276 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
277 |
278 |
279 | 13
280 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 | 10005
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
299 |
300 |
301 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
302 |
303 |
304 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
305 |
306 |
307 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
308 |
309 |
310 | 13
311 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
325 |
326 |
327 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
328 |
329 |
330 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
331 |
332 |
333 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
334 |
335 |
336 | 13
337 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 | 10006
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
356 |
357 |
358 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
359 |
360 |
361 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
362 |
363 |
364 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
365 |
366 |
367 | 13
368 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
382 |
383 |
384 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
385 |
386 |
387 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
388 |
389 |
390 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
391 |
392 |
393 | 13
394 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 | 10007
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
413 |
414 |
415 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
416 |
417 |
418 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
419 |
420 |
421 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
422 |
423 |
424 | 13
425 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
439 |
440 |
441 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
442 |
443 |
444 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
445 |
446 |
447 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
448 |
449 |
450 | 13
451 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 | 5
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
470 |
471 |
472 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
473 |
474 |
475 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
476 |
477 |
478 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
479 |
480 |
481 | 13
482 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 | 10008
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
501 |
502 |
503 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
504 |
505 |
506 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
507 |
508 |
509 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
510 |
511 |
512 | 13
513 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
527 |
528 |
529 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
530 |
531 |
532 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
533 |
534 |
535 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
536 |
537 |
538 | 13
539 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 | 10009
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 | com.atlassian.jira.workflow.function.issue.UpdateIssueStatusFunction
558 |
559 |
560 | com.atlassian.jira.workflow.function.misc.CreateCommentFunction
561 |
562 |
563 | com.atlassian.jira.workflow.function.issue.GenerateChangeHistoryFunction
564 |
565 |
566 | com.atlassian.jira.workflow.function.issue.IssueReindexFunction
567 |
568 |
569 | 13
570 | com.atlassian.jira.workflow.function.event.FireIssueEventFunction
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
--------------------------------------------------------------------------------
/asset/jira-cloud-status.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-securityhub-jira-software-integration/65898ab05357015fb39718d7f78aae2d38303368/asset/jira-cloud-status.png
--------------------------------------------------------------------------------
/asset/jira-cloud-transitions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-securityhub-jira-software-integration/65898ab05357015fb39718d7f78aae2d38303368/asset/jira-cloud-transitions.png
--------------------------------------------------------------------------------
/asset/workflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-securityhub-jira-software-integration/65898ab05357015fb39718d7f78aae2d38303368/asset/workflow.png
--------------------------------------------------------------------------------
/conf/params_prod.sh:
--------------------------------------------------------------------------------
1 | # Stack parameter
2 | export ORG_ACCOUNT_ID='' # ID for Organization Management account
3 | export ORG_ROLE=OrganizationsReadOnlyAccess
4 | export AWS_REGION=eu-west-1
5 | export EXTERNAL_ID='' #Optional
6 | export JIRA_DEFAULT_ASSIGNEE='' #ID for default assignee for all Security Issues
7 | export JIRA_INSTANCE="" #HTTPS address for JIRA server (exclude schema "https://")
8 | export JIRA_PROJECT_KEY="" # JIRA Project Key
9 | export ISSUE_TYPE="" #JIRA Issuetype name: Example, "Bug", "Security Issue"
10 | export REGIONS=("eu-west-1") # List of regions deployed
11 |
12 | PARAMETERS=(
13 | "OrganizationManagementAccountId=$ORG_ACCOUNT_ID"
14 | "JIRADefaultAssignee=$JIRA_DEFAULT_ASSIGNEE"
15 | "OrganizationAccessExternalId=$EXTERNAL_ID"
16 | "AutomatedChecks=$AUTOMATED_CHECKS"
17 | "JIRAInstance=$JIRA_INSTANCE"
18 | "JIRAIssueType"="$ISSUE_TYPE"
19 | "JIRAProjectKey"="$JIRA_PROJECT_KEY"
20 | )
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -eio pipefail
3 |
4 | function print_usage (){
5 | echo "------------------------------------------------------------------------------"
6 | echo " This script deploys the code without pipeline"
7 | echo "------------------------------------------------------------------------------"
8 | echo "Usage: ./deploy.sh [env]"
9 | echo "For example 1: ./deploy.sh prod"
10 | echo "For example 2: ./deploy.sh local"
11 | echo
12 | echo "environment points to config file to load"
13 | }
14 |
15 | # Load the config
16 | if [ -z $1 ]; then
17 | echo "Assuming params already configured"
18 | else
19 | echo "loading params from \"conf/params_${1}.sh\" with environment variables"
20 | . conf/params_${1}.sh
21 | fi
22 |
23 | for regx in ${REGIONS[@]}; do
24 |
25 | ENVIRONMENT="$1"
26 | export AWS_REGION=$regx
27 | ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
28 | BASE="securityhub-jira-$1"
29 |
30 | function ensure_basic {
31 | $AWS_CFN deploy --stack-name "${BASE}-artifact" --template-file src/template/lpt-basic.yaml --no-fail-on-empty-changeset --parameter-overrides BucketName="artifact-securityhub-jira-$ENVIRONMENT-$AWS_REGION-$ACCOUNT"
32 | BUCKET=$($AWS_CFN describe-stacks --stack-name "${BASE}-artifact" --query Stacks[].Outputs[*].[OutputKey,OutputValue] --output text | grep LptBucket | awk '{ print $2 }')
33 | }
34 |
35 | function ensure_environment_variable_is_set {
36 | test -n "${!1}" || { echo >&2 "Required environment variable '$1' not found. Aborting."; exit 1; }
37 | }
38 |
39 | ensure_environment_variable_is_set AWS_REGION
40 |
41 | export AWS_CFN="aws cloudformation --region $AWS_REGION"
42 |
43 | ensure_basic
44 |
45 | # Clean up dist folder
46 | rm -r dist/* || mkdir -p dist
47 |
48 | # Restore .gitignore
49 | #echo '*' > dist/.gitignore
50 |
51 | # Copy new versions of Lambda
52 | cp -r src/* dist/
53 |
54 | # Install JIRA from master (assign_issue GDPR)
55 | INSTALL_PREREQUISITES=$(cd dist/code && pip3 install -t . jira==3.1.1)
56 |
57 | # ZIP Dependency
58 | INSTALL_LAMBDA=$(cd dist/code && zip -r9 ../lambda.zip ./)
59 |
60 | # Package cloudformation into S3 artifact
61 | $AWS_CFN package --template src/template/cloudformation_template.yaml --s3-bucket "$BUCKET" --output-template-file dist/template/deployment-version.yaml
62 |
63 | # Validate template
64 | $AWS_CFN validate-template --template-body file://$(pwd)/dist/template/deployment-version.yaml > /dev/null || exit 1
65 |
66 | $AWS_CFN deploy --template-file dist/template/deployment-version.yaml --capabilities CAPABILITY_NAMED_IAM --stack-name "$BASE-solution" --parameter-overrides "${PARAMETERS[@]}"
67 |
68 | done
--------------------------------------------------------------------------------
/src/code/config/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "Controls" : {
3 | "eu-west-1": [
4 | "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0/rule/1.22"
5 | ],
6 | "default": [
7 | ]
8 | }
9 | }
--------------------------------------------------------------------------------
/src/code/security_hub_integration.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | import logging
5 | import json
6 | import os
7 | import boto3
8 | import sys
9 | from jira import JIRA
10 | import utils
11 | from datetime import datetime, timezone
12 |
13 | # set global logger
14 | logger = logging.getLogger('')
15 | logger.setLevel(logging.INFO)
16 | logging.getLogger().addHandler(logging.StreamHandler())
17 |
18 | securityhub = boto3.client('securityhub')
19 | secretsmanager = boto3.client('secretsmanager')
20 |
21 |
22 | def finding_parser(finding):
23 | account = finding["AwsAccountId"]
24 | description = finding["Description"]
25 | severity = finding["Severity"]["Label"]
26 | title = finding["Title"]
27 | finding_id = finding["Id"]
28 | product_arn = finding["ProductArn"]
29 | resources = [resource.get('Id') for resource in finding["Resources"]]
30 | status = finding["Workflow"]["Status"]
31 | recordstate = finding["RecordState"]
32 |
33 | return account, description, severity, title, finding_id, product_arn, resources, status, recordstate # returns data
34 |
35 |
36 | def create_jira(jira_client, project_key, issuetype_name, product_arn, account, region, description, resources, severity, title, id):
37 |
38 | resources = "Resources: %s" % resources if not "default" in product_arn else ""
39 |
40 | new_issue = utils.create_ticket(
41 | jira_client, project_key, issuetype_name, account, region, description, resources, severity, title, id)
42 | utils.update_securityhub(
43 | securityhub, id, product_arn, "NOTIFIED", 'JIRA Ticket: {0}'.format(new_issue))
44 | utils.update_jira_assignee(jira_client, new_issue, account)
45 |
46 |
47 | def is_automated_check(finding):
48 | script_dir=os.path.dirname(__file__)
49 | with open(os.path.join(script_dir, "config/config.json")) as config_file:
50 | automated_controls=json.load(config_file)
51 | region=os.environ['AWS_REGION']
52 | if region in automated_controls["Controls"]:
53 | return finding["GeneratorId"] in automated_controls["Controls"][region]
54 | else:
55 | return finding["GeneratorId"] in automated_controls["Controls"]["default"]
56 |
57 | def lambda_handler(event, context): # Main function
58 | utils.validate_environments(
59 | ["JIRA_API_TOKEN", "AWS_REGION"])
60 |
61 | account_id=event["account"]
62 | region=os.environ['AWS_REGION']
63 | os.environ.get("JIRA_CREDENTIALS")
64 | project_key=os.environ['JIRA_PROJECT_KEY']
65 | issuetype_name=os.environ['JIRA_ISSUETYPE']
66 | jira_instance = os.environ['JIRA_INSTANCE']
67 | jira_credentials = os.environ.get("JIRA_API_TOKEN")
68 |
69 | for finding in event["detail"]["findings"]:
70 | account, description, severity, title, finding_id, product_arn, resources, status, recordstate=finding_parser(
71 | finding)
72 | try:
73 | if event["detail-type"] == "Security Hub Findings - Custom Action" and event["detail"]["actionName"] == "CreateJiraIssue":
74 | if status != "NEW":
75 | raise UserWarning(
76 | "Finding workflow is not NEW: %s" % finding_id)
77 | if recordstate != "ACTIVE":
78 | raise UserWarning("Finding is not ACTIVE: %s" % finding_id)
79 | jira_client=utils.get_jira_client(secretsmanager,jira_instance,jira_credentials)
80 | jira_issue=utils.get_jira_finding(
81 | jira_client, finding_id, project_key, issuetype_name)
82 | if not jira_issue:
83 | logger.info(
84 | "Creating ticket manually for {0}".format(finding_id))
85 | create_jira(jira_client, project_key, issuetype_name, product_arn, account,
86 | region, description, resources, severity, title, finding_id)
87 | else:
88 | logger.info("Finding {0} already reported in ticket {1}".format(
89 | finding_id, jira_issue))
90 | elif event["detail-type"] == "Security Hub Findings - Imported":
91 | if recordstate == "ARCHIVED" and status == "NOTIFIED":
92 | # Move to resolved
93 | jira_client=utils.get_jira_client(secretsmanager,jira_instance,jira_credentials)
94 | jira_issue=utils.get_jira_finding(
95 | jira_client, finding_id, project_key, issuetype_name)
96 |
97 | if(jira_issue):
98 | utils.close_jira_issue(jira_client, jira_issue)
99 | utils.update_securityhub(securityhub, finding_id, product_arn, "RESOLVED",
100 | 'Closed JIRA Ticket {0}'.format(jira_issue))
101 | elif recordstate == "ACTIVE" and status == "RESOLVED":
102 | # Move to resolved
103 | jira_client=utils.get_jira_client(secretsmanager,jira_instance,jira_credentials)
104 | jira_issue=utils.get_jira_finding(
105 | jira_client, finding_id, project_key, issuetype_name)
106 |
107 | if(jira_issue) and utils.is_closed(jira_client, jira_issue):
108 | # Reopen closed ticket as it was re-detected
109 | utils.reopen_jira_issue(jira_client, jira_issue)
110 | utils.update_securityhub(securityhub, finding_id, product_arn, "NOTIFIED",
111 | 'Reopening JIRA Ticket {0}'.format(jira_issue))
112 | elif recordstate == "ACTIVE" and status == "NEW" and is_automated_check(finding):
113 | # Check if in automatically list of findings to create automatically
114 | jira_client=utils.get_jira_client(secretsmanager,jira_instance,jira_credentials)
115 | jira_issue=utils.get_jira_finding(
116 | jira_client, finding_id, project_key, issuetype_name)
117 |
118 | if not jira_issue:
119 | logger.info(
120 | "Creating ticket automatically for {0}".format(finding_id))
121 | create_jira(jira_client, project_key, issuetype_name, product_arn, account,
122 | region, description, resources, severity, title, finding_id)
123 |
124 | else:
125 | logger.info(
126 | "Not performing any action for {}".format(finding_id))
127 | else:
128 | logger.info("Unknown custom action {} {}".format(
129 | event["detail-type"], event["detail"]["actionName"]))
130 | except UserWarning as e:
131 | logger.error(e)
132 |
133 |
134 | if __name__ == "__main__":
135 | if not len(sys.argv) - 1 > 0:
136 | print("Usage: python security_hub_integration.py event.template")
137 | template=sys.argv[1]
138 | with open(template, "r") as event_file:
139 | security_hub_event=json.load(event_file)
140 | local_time=datetime.now(timezone.utc).astimezone().isoformat()
141 | for securityhub_finding in security_hub_event["detail"]["findings"]:
142 | securityhub_finding["UpdatedAt"]=local_time
143 | lambda_handler(security_hub_event, None)
144 |
--------------------------------------------------------------------------------
/src/code/sync_securityhub.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | import logging
5 | import sys
6 | import os
7 | import boto3
8 | from jira import JIRA
9 | import utils
10 |
11 | sys.path.append('lib')
12 |
13 | logger = logging.getLogger('')
14 | logger.setLevel(logging.INFO)
15 | logging.getLogger().addHandler(logging.StreamHandler())
16 |
17 | securityhub = boto3.client('securityhub')
18 | secretsmanager = boto3.client('secretsmanager')
19 |
20 |
21 | def lambda_handler(event, context):
22 | utils.validate_environments(
23 | ["JIRA_API_TOKEN", "AWS_REGION"])
24 |
25 | region = os.environ['AWS_REGION']
26 | jira_instance = os.environ['JIRA_INSTANCE']
27 | jira_credentials = os.environ.get("JIRA_API_TOKEN")
28 | project_key = os.environ['JIRA_PROJECT_KEY']
29 | issuetype_name = os.environ['JIRA_ISSUETYPE']
30 |
31 | jira_client = utils.get_jira_client(secretsmanager,jira_instance,jira_credentials)
32 | latest = utils.get_jira_latest_updated_findings(
33 | jira_client, project_key, issuetype_name)
34 | for ticket in latest:
35 | try:
36 | logger.info("Checking {0}".format(ticket))
37 | finding_id = utils.get_finding_id_from(ticket)
38 | if finding_id:
39 | results = securityhub.get_findings(Filters={"Id": [{
40 | 'Value': finding_id,
41 | 'Comparison': 'EQUALS'
42 | }]}
43 | )
44 | if len(results["Findings"]) > 0:
45 | finding = results["Findings"][0]
46 | finding_status = finding["Workflow"]["Status"]
47 | product_arn = finding["ProductArn"]
48 | record_state = finding["RecordState"]
49 |
50 | if utils.is_suppressed(jira_client, ticket) and finding_status != "SUPPRESSED":
51 | # If accepted or false positive in JIRA, mark as suppressed in Security Hub
52 | logger.info("Suppress {0} based on {1}".format(
53 | finding_id, ticket))
54 | utils.update_securityhub(
55 | securityhub, finding_id, product_arn, "SUPPRESSED", 'JIRA Ticket: {0}'.format(ticket))
56 |
57 | if utils.is_closed(jira_client, ticket) and finding_status != "RESOLVED":
58 | # If closed in JIRA, mark as Resolved in Security Hub
59 | logger.info("Marking as resolved {0} based on {1}".format(
60 | finding_id, ticket))
61 | utils.update_securityhub(
62 | securityhub, finding_id, product_arn, "RESOLVED", 'JIRA Ticket was resolved')
63 |
64 | if not utils.is_closed(jira_client, ticket) and not utils.is_suppressed(jira_client, ticket):
65 | if record_state != "ARCHIVED" and finding_status != "NOTIFIED":
66 | # If Security Hub finding is still ACTIVE but supressed and JIRA is not closed, move back to NOTIFIED
67 | logger.info("Reopen {0} based on {1}".format(
68 | finding_id, ticket))
69 | utils.update_securityhub(
70 | securityhub, finding_id, product_arn, "NOTIFIED", 'JIRA Ticket: {0}'.format(ticket))
71 |
72 | if record_state == "ARCHIVED" and finding_status != "RESOLVED":
73 | # If Security Hub finding is still ARCHIVED, then it was resolved, close JIRA issue and resolve Security Hub
74 | logger.info("Closing {1} based on {0} archived status".format(
75 | finding_id, ticket))
76 | utils.close_jira_issue(jira_client, ticket)
77 | utils.update_securityhub(
78 | securityhub, finding_id, product_arn, "RESOLVED", 'Closed JIRA Ticket {0}'.format(ticket))
79 | else:
80 | raise UserWarning(
81 | "aws-sec label found for {0} but couldn't find the related Security Hub finding".format(ticket))
82 | except UserWarning as e:
83 | logger.error(e)
84 |
85 |
86 | if __name__ == "__main__":
87 | lambda_handler(None, None)
88 |
--------------------------------------------------------------------------------
/src/code/test_securityhub_integration.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | import unittest
5 | from unittest.mock import patch, MagicMock
6 | from datetime import datetime, timezone
7 | import json
8 | import security_hub_integration
9 | import warnings
10 | import utils
11 | import logging
12 |
13 | class TestSecurityHubtoJiraIntegration(unittest.TestCase):
14 |
15 | def setUp(self):
16 | # https://github.com/boto/boto3/issues/454
17 | warnings.filterwarnings(
18 | "ignore", category=ResourceWarning, message="unclosed.*")
19 |
20 | def load_test(self, template):
21 | with open(template, "r") as event_file:
22 | event = json.load(event_file)
23 | local_time = datetime.now(timezone.utc).astimezone().isoformat()
24 | for finding in event["detail"]["findings"]:
25 | finding["UpdatedAt"] = local_time
26 | return event
27 |
28 | @patch('security_hub_integration.utils.get_jira_client')
29 | @patch('security_hub_integration.utils.create_ticket')
30 | @patch('security_hub_integration.utils.update_securityhub')
31 | @patch('security_hub_integration.utils.update_jira_assignee')
32 | def test_custom_action_new_finding(self, get_jira_client, create_ticket, update_securityhub, update_jira_assignee):
33 | event = self.load_test('test/custom_new.template')
34 | get_jira_client.return_value = {}
35 | security_hub_integration.lambda_handler(event, None)
36 | create_ticket.assert_called_once()
37 | update_securityhub.assert_called_once()
38 | update_jira_assignee.assert_called_once()
39 |
40 | @patch('security_hub_integration.utils.update_securityhub')
41 | @patch('security_hub_integration.utils.close_jira_issue')
42 | def test_imported_archived_new_finding(self, update_securityhub, close_jira_issue):
43 | event = self.load_test('test/imported_archived_new.template')
44 | security_hub_integration.lambda_handler(event, None)
45 | update_securityhub.assert_not_called()
46 | close_jira_issue.assert_not_called()
47 |
48 | @patch('security_hub_integration.utils.get_jira_client')
49 | @patch('security_hub_integration.utils.get_jira_finding')
50 | @patch('security_hub_integration.utils.update_securityhub')
51 | @patch('security_hub_integration.utils.close_jira_issue')
52 | def test_imported_archived_existing_ticket(self, get_jira_client, get_jira_finding, update_securityhub, close_jira_issue):
53 | event = self.load_test(
54 | 'test/imported_archived_existing.template')
55 | get_jira_client.return_value = {}
56 | get_jira_finding.return_value = ["Mock-123"]
57 | security_hub_integration.lambda_handler(event, None)
58 | update_securityhub.assert_called()
59 | close_jira_issue.assert_called()
60 |
61 | @patch('security_hub_integration.utils.get_jira_client')
62 | @patch('security_hub_integration.utils.create_ticket')
63 | @patch('security_hub_integration.utils.update_securityhub')
64 | @patch('security_hub_integration.utils.update_jira_assignee')
65 | def test_imported_new_automated_standard_finding(self, get_jira_client, create_ticket, update_securityhub, update_jira_assignee):
66 | event = self.load_test(
67 | 'test/imported_new_automated_standard_check.template')
68 | get_jira_client.return_value = {}
69 | security_hub_integration.lambda_handler(event, None)
70 | create_ticket.assert_called()
71 | update_securityhub.assert_called()
72 | update_jira_assignee.assert_called()
73 |
74 | @patch('security_hub_integration.utils.create_ticket')
75 | @patch('security_hub_integration.utils.update_securityhub')
76 | @patch('security_hub_integration.utils.update_jira_assignee')
77 | def test_imported_new_not_automated_finding(self, create_ticket, update_securityhub, update_jira_assignee):
78 | event = self.load_test('test/imported_new_notautomated.template')
79 | security_hub_integration.lambda_handler(event, None)
80 | create_ticket.assert_not_called()
81 | update_securityhub.assert_not_called()
82 | update_jira_assignee.assert_not_called()
83 |
84 |
85 | if __name__ == '__main__':
86 | unittest.main()
87 |
--------------------------------------------------------------------------------
/src/code/utils.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | import logging
5 | import os
6 | import hashlib
7 | import base64
8 | import re
9 | import boto3
10 | import json
11 | from jira import JIRA, JIRAError
12 | from botocore.exceptions import ClientError
13 | import jira
14 |
15 | logger = logging.getLogger('')
16 |
17 |
18 | def validate_environments(envs):
19 | undefined = []
20 |
21 | for env in envs:
22 | is_defined = env in os.environ
23 | if not is_defined:
24 | undefined.append(env)
25 | logger.error('Environment variable %s not set', env)
26 | if len(undefined) > 0:
27 | raise UserWarning(
28 | "Missing environment variables: {}".format(",".join(undefined)))
29 |
30 |
31 | def assume_role(aws_account_number, role_name, external_id=None):
32 | """
33 | Assumes the provided role in each account and returns a GuardDuty client
34 | :param aws_account_number: AWS Account Number
35 | :param role_name: Role to assume in target account
36 | """
37 | sts_client = boto3.client('sts')
38 | partition = sts_client.get_caller_identity()['Arn'].split(":")[1]
39 |
40 | parameters = {"RoleArn": 'arn:{}:iam::{}:role/{}'.format(
41 | partition,
42 | aws_account_number,
43 | role_name,
44 | ), "RoleSessionName": "SecurityScanner"}
45 |
46 | if external_id:
47 | parameters["ExternalId"] = external_id
48 | response = sts_client.assume_role(**parameters)
49 |
50 | account_session = boto3.Session(
51 | aws_access_key_id=response['Credentials']['AccessKeyId'],
52 | aws_secret_access_key=response['Credentials']['SecretAccessKey'],
53 | aws_session_token=response['Credentials']['SessionToken'])
54 |
55 | session = {}
56 | session['session'] = account_session
57 | session['aws_access_key_id'] = response['Credentials']['AccessKeyId']
58 | session['aws_secret_access_key'] = response['Credentials']['SecretAccessKey']
59 | session['aws_session_token'] = response['Credentials']['SessionToken']
60 | return session
61 |
62 |
63 | def update_unassigned_ticket(jira_client, issue, message):
64 | jira_client.assign_issue(issue, os.environ.get("JIRA_DEFAULT_ASSIGNEE"))
65 | issue.fields.labels.append("aws-sec-not-assigned")
66 | issue.update(fields={"labels": issue.fields.labels})
67 | jira_client.add_comment(issue, message)
68 |
69 |
70 | def get_account_organization_tags(account):
71 | org_id = os.environ.get("ORG_ACCOUNT_ID")
72 | org_role = os.environ.get("ORG_ROLE")
73 | external_id = os.environ.get("EXTERNAL_ID")
74 | if org_role:
75 | session = assume_role(org_id, org_role, external_id)['session']
76 | org_client = session.client('organizations')
77 | tags = org_client.list_tags_for_resource(ResourceId=account)
78 | return tags
79 | return {}
80 |
81 | # assign ticket based on Organization account
82 |
83 |
84 | def update_jira_assignee(jira_client, issue, account):
85 | tags = get_account_organization_tags(account)
86 | merged_tags = {}
87 | for tag in tags['Tags']:
88 | merged_tags[tag['Key']] = tag['Value']
89 | if merged_tags.get("SecurityContactID"):
90 | assignee = merged_tags.get("SecurityContactID")
91 | try:
92 | jira_client.assign_issue(issue, assignee)
93 | except JIRAError:
94 | logger.warning("User {0} couldn't be assigned to {1}".format(
95 | assignee, jira_client))
96 | message = "Security responsible not in JIRA\n Id: {0}".format(
97 | assignee)
98 | update_unassigned_ticket(jira_client, issue, message)
99 | else:
100 | logger.info("Account owner could not be identified {0} - {1}".format(account,issue))
101 | message = "Account owner could not be identified"
102 | update_unassigned_ticket(jira_client, issue, message)
103 |
104 |
105 | def get_finding_id_from(jira_ticket):
106 | if jira_ticket is None or jira_ticket.fields.description is None:
107 | logger.warning("The jira_ticket or its description is None, cannot extract finding ID.")
108 | return None
109 |
110 | description = jira_ticket.fields.description
111 | # Searching for regex in description
112 | matched = re.search(
113 | 'Id%3D%255Coperator%255C%253AEQUALS%255C%253A([a-zA-Z0-9\\.\\-\\_\\:\\/]+)', description)
114 | return matched.group(1) if matched and matched.group(1) else None
115 |
116 | def get_jira_client(secretsmanager_client,jira_instance,jira_credentials_secret):
117 | region = os.environ['AWS_REGION']
118 | jira_credentials = get_secret(secretsmanager_client, jira_credentials_secret, region)
119 | auth_type = jira_credentials['auth']
120 | jira_client = None
121 | if auth_type == "basic_auth":
122 | jira_client=JIRA("https://"+jira_instance, basic_auth=(jira_credentials['email'], jira_credentials['token']))
123 | else:
124 | jira_client=JIRA(jira_instance, token_auth=jira_credentials['token'])
125 |
126 | return jira_client
127 |
128 |
129 | def get_finding_digest(finding_id):
130 | m = hashlib.md5() # nosec
131 | m.update(finding_id.encode("utf-8"))
132 | one_way_digest = m.hexdigest()
133 | return one_way_digest
134 |
135 |
136 | def get_jira_finding(jira_client, finding_id,project_key, issuetype_name):
137 | digest = get_finding_digest(finding_id)
138 | created_before = jira_client.search_issues(
139 | 'Project = {0} AND issuetype = "{1}" AND (labels = aws-sec-{2})'.format(project_key, issuetype_name,digest))
140 | # Should only exist once
141 | return created_before[0] if len(created_before) > 0 else None
142 |
143 | def get_jira_latest_updated_findings(jira_client,project_key, issuetype_name):
144 | return jira_client.search_issues('Project = {0} AND issuetype = "{1}" AND updated >= -2w'.format(project_key, issuetype_name), maxResults=False)
145 |
146 | # creates ticket based on the Security Hub finding
147 | def create_ticket(jira_client, project_key, issuetype_name, account, region, description, resources, severity, title, id):
148 | digest = get_finding_digest(id)
149 |
150 | finding_link = "https://{0}.console.aws.amazon.com/securityhub/home?region={0}#/findings?search=Id%3D%255Coperator%255C%253AEQUALS%255C%253A{1}".format(
151 | region, id)
152 | issue_dict = {
153 | "project": {"key": project_key},
154 | "issuetype": {"name": issuetype_name},
155 | "summary": "AWS Security Issue :: {} :: {} :: {}".format(account, region, title),
156 | "labels": ["aws-sec-%s" % digest],
157 | "priority": {"name": severity.capitalize()},
158 | "description": """ *What is the problem?*
159 | We detected a security finding within the AWS account {} you are responsible for.
160 | {}
161 |
162 | {}
163 |
164 | [Link to Security Hub finding|{}]
165 |
166 | *What do I need to do with the ticket?*
167 | * Access the account and verify the configuration.
168 | Acknowledge working on ticket by moving it to "Allocated for Fix".
169 | Once fixed, moved to test fix so Security validates the issue is addressed.
170 | * If you think risk should be accepted, move it to "Awaiting Risk acceptance".
171 | This will require review by a Security engineer.
172 | * If you think is a false positive, transition it to "Mark as False Positive".
173 | This will get reviewed by a Security engineer and reopened/closed accordingly.
174 | """.format(account, resources, description, finding_link)
175 | }
176 | new_issue = jira_client.create_issue(
177 | fields=issue_dict) # writes dict to jira
178 | return new_issue
179 |
180 |
181 | def update_securityhub(securityhub_client, id, product_arn, status, note):
182 | response = securityhub_client.batch_update_findings(
183 | FindingIdentifiers=[
184 | {'Id': id,
185 | 'ProductArn': product_arn
186 | }],
187 | Workflow={'Status': status}, Note={
188 | 'Text': note,
189 | 'UpdatedBy': 'security-hub-integration'
190 | })
191 | if response.get('FailedFindings'):
192 | for element in response['FailedFindings']:
193 | logger.error("Update error - FindingId {0}".format(element["Id"]))
194 | logger.error(
195 | "Update error - ErrorCode {0}".format(element["ErrorCode"]))
196 | logger.error(
197 | "Update error - ErrorMessage {0}".format(element["ErrorMessage"]))
198 |
199 |
200 | def is_closed(jira_client, issue):
201 | return issue.fields.status.name == "Resolved"
202 |
203 |
204 | def is_suppressed(jira_client, issue):
205 | return issue.fields.status.name == "Risk approved" or issue.fields.status.name == "Accepted false positive"
206 |
207 |
208 | def is_test_fix(jira_client, issue):
209 | return issue.fields.status.name == "Test fix"
210 |
211 |
212 | def reopen_jira_issue(jira_client, issue):
213 | jira_client.transition_issue(issue, 'Reopen')
214 |
215 |
216 | def close_jira_issue(jira_client, issue):
217 | status = issue.fields.status.name
218 | if status in ["Open"]:
219 | jira_client.transition_issue(issue, "Allocate for fix")
220 | if status in ["Open", "Allocated for fix"]:
221 | jira_client.transition_issue(issue, "Mark for testing")
222 | if status in ["Open", "Allocated for fix", "Test fix"]:
223 | jira_client.transition_issue(issue, "Mark as resolved", comment="Resolved automatically by security-hub-integration")
224 | else:
225 | logger.error(
226 | "Cannot transition issue {0} as it's either marked as closed, awaiting risk acceptance or as false positive".format(issue))
227 |
228 |
229 | def get_secret(client, secret_arn, region_name):
230 |
231 | secret = None
232 | try:
233 | get_secret_value_response = client.get_secret_value(
234 | SecretId=secret_arn
235 | )
236 | except ClientError as e:
237 | if e.response['Error']['Code'] == 'DecryptionFailureException':
238 | raise e
239 | elif e.response['Error']['Code'] == 'InternalServiceErrorException':
240 | raise e
241 | elif e.response['Error']['Code'] == 'InvalidParameterException':
242 | raise e
243 | elif e.response['Error']['Code'] == 'InvalidRequestException':
244 | raise e
245 | elif e.response['Error']['Code'] == 'ResourceNotFoundException':
246 | raise e
247 | else:
248 | if 'SecretString' in get_secret_value_response:
249 | secret = get_secret_value_response['SecretString']
250 | else:
251 | secret = base64.b64decode(
252 | get_secret_value_response['SecretBinary'])
253 | return json.loads(secret)
254 |
--------------------------------------------------------------------------------
/src/template/cloudformation_template.yaml:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | AWSTemplateFormatVersion: 2010-09-09
5 | Description: Bidirectional integration of Security Hub with JIRA (v1.0)
6 |
7 | Parameters:
8 | ScheduleExpression:
9 | Type: String
10 | Default: "rate(1 day)"
11 | Environment:
12 | Description: Environment Name. Used for tagging.
13 | Type: String
14 | Default: prod
15 | OrganizationAccessRole:
16 | Default: OrganizationsReadOnlyAccess
17 | Type: String
18 | Description: Role to assume the organization account to read organization tags
19 | OrganizationAccessExternalId:
20 | Type: String
21 | Description: External Id used to assume role to organization management account
22 | Default: ''
23 | OrganizationManagementAccountId:
24 | Description: Organization management account Id
25 | Type: String
26 | MinLength: 12
27 | MaxLength: 12
28 | JIRADefaultAssignee:
29 | Description: JIRA User Id for default assignee
30 | Type: String
31 | JIRAInstance:
32 | Description: JIRA Instance URL
33 | Type: String
34 | JIRAProjectKey:
35 | Description: JIRA Project Key
36 | Type: String
37 | JIRAIssueType:
38 | Description: JIRA Issue Type
39 | Type: String
40 |
41 | Resources:
42 | LambdaImportRole:
43 | Type: "AWS::IAM::Role"
44 | Properties:
45 | Description: "Lambda role for importing findings from Security Hub to JIRA"
46 | AssumeRolePolicyDocument:
47 | Version: 2012-10-17
48 | Statement:
49 | - Effect: Allow
50 | Principal:
51 | Service:
52 | - lambda.amazonaws.com
53 | Action:
54 | - "sts:AssumeRole"
55 | Policies:
56 | - PolicyName: !Sub securityhub-jira-lambda-import-${Environment}-${AWS::Region}
57 | PolicyDocument:
58 | Statement:
59 | - Effect: Allow
60 | Action:
61 | - "securityhub:BatchImportFindings"
62 | - "securityhub:UpdateFindings"
63 | - "securityhub:BatchUpdateFindings"
64 | - "securityhub:GetFindings"
65 | Resource:
66 | - "*"
67 | - Effect: Allow
68 | Action:
69 | - "logs:CreateLogGroup"
70 | - "logs:CreateLogStream"
71 | - "logs:PutLogEvents"
72 | Resource: "*"
73 | - Effect: Allow
74 | Action:
75 | - "sts:AssumeRole"
76 | Resource:
77 | - !Sub "arn:aws:iam::*:role/${OrganizationAccessRole}"
78 | - Effect: Allow
79 | Action:
80 | - secretsmanager:GetResourcePolicy
81 | - secretsmanager:GetSecretValue
82 | - secretsmanager:DescribeSecret
83 | - secretsmanager:ListSecretVersionIds
84 | Resource:
85 | - !Ref JIRAAPITokenSecret
86 |
87 |
88 | LambdaRefreshRole:
89 | Type: "AWS::IAM::Role"
90 | Properties:
91 | Description: "Lambda role for refreshing findings in JIRA and Security Hub"
92 | AssumeRolePolicyDocument:
93 | Version: 2012-10-17
94 | Statement:
95 | - Effect: Allow
96 | Principal:
97 | Service:
98 | - lambda.amazonaws.com
99 | Action:
100 | - "sts:AssumeRole"
101 | Policies:
102 | - PolicyName: !Sub securityhub-jira-lambda-refresh-${Environment}-${AWS::Region}
103 | PolicyDocument:
104 | Statement:
105 | - Effect: Allow
106 | Action:
107 | - "securityhub:BatchImportFindings"
108 | - "securityhub:UpdateFindings"
109 | - "securityhub:BatchUpdateFindings"
110 | - "securityhub:GetFindings"
111 | Resource:
112 | - "*"
113 | - Effect: Allow
114 | Action:
115 | - "logs:CreateLogGroup"
116 | - "logs:CreateLogStream"
117 | - "logs:PutLogEvents"
118 | Resource: "*"
119 | - Effect: Allow
120 | Action:
121 | - secretsmanager:GetResourcePolicy
122 | - secretsmanager:GetSecretValue
123 | - secretsmanager:DescribeSecret
124 | - secretsmanager:ListSecretVersionIds
125 | Resource:
126 | - !Ref JIRAAPITokenSecret
127 |
128 | JIRASecHubCWRule:
129 | Type: AWS::Events::Rule
130 | Properties:
131 | Description: This CW rule helps keep Security Hub in sync with JIRA updates
132 | Name: !Sub securityhub-change-status-${Environment}
133 | EventPattern:
134 | source:
135 | - aws.securityhub
136 | detail-type:
137 | - Security Hub Findings - Custom Action
138 | - Security Hub Findings - Imported
139 | State: "ENABLED"
140 | Targets:
141 | - Arn: !GetAtt JIRASecHubFunction.Arn
142 | Id: "TargetFunctionV1"
143 |
144 | JIRAAPITokenSecret:
145 | Type: "AWS::SecretsManager::Secret"
146 | Properties:
147 | Name: !Sub JiraAPIToken-${Environment}
148 | Description: "JIRA API Token"
149 |
150 | PermissionForEventsToInvokeIntegrationLambda:
151 | Type: AWS::Lambda::Permission
152 | Properties:
153 | FunctionName:
154 | Ref: "JIRASecHubFunction"
155 | Action: "lambda:InvokeFunction"
156 | Principal: "events.amazonaws.com"
157 | SourceArn:
158 | Fn::GetAtt:
159 | - "JIRASecHubCWRule"
160 | - "Arn"
161 |
162 | JIRASecHubFunction:
163 | Type: AWS::Lambda::Function
164 | Properties:
165 | FunctionName: !Sub securityhub-jira-lambda-import-${Environment}
166 | Description: Lambda integrates Security Hub to JIRA
167 | Handler: "security_hub_integration.lambda_handler"
168 | Role:
169 | Fn::GetAtt:
170 | - LambdaImportRole
171 | - Arn
172 | Runtime: python3.11
173 | Timeout: 300
174 | Code: ../../dist/lambda.zip
175 | Environment:
176 | Variables:
177 | JIRA_API_TOKEN: !Ref JIRAAPITokenSecret
178 | ORG_ACCOUNT_ID: !Ref OrganizationManagementAccountId
179 | ORG_ROLE: !Ref OrganizationAccessRole
180 | EXTERNAL_ID: !Ref OrganizationAccessExternalId
181 | JIRA_DEFAULT_ASSIGNEE: !Ref JIRADefaultAssignee
182 | JIRA_ISSUETYPE: !Ref JIRAIssueType
183 | JIRA_PROJECT_KEY: !Ref JIRAProjectKey
184 | JIRA_INSTANCE: !Ref JIRAInstance
185 |
186 | RefreshJIRASecHubCWRule:
187 | Type: AWS::Events::Rule
188 | Properties:
189 | Description: Keep Security Hub findings in sync with JIRA updates
190 | Name: !Sub securityhub-jira-refresh-${Environment}
191 | ScheduleExpression: !Ref ScheduleExpression
192 | State: "ENABLED"
193 | Targets:
194 | - Arn: !GetAtt RefreshJIRASecHubFunction.Arn
195 | Id: "TargetFunctionV1"
196 |
197 | PermissionForEventsToInvokeRefreshLambda:
198 | Type: AWS::Lambda::Permission
199 | Properties:
200 | FunctionName:
201 | Ref: "RefreshJIRASecHubFunction"
202 | Action: "lambda:InvokeFunction"
203 | Principal: "events.amazonaws.com"
204 | SourceArn:
205 | Fn::GetAtt:
206 | - "RefreshJIRASecHubCWRule"
207 | - "Arn"
208 |
209 | RefreshJIRASecHubFunction:
210 | Type: AWS::Lambda::Function
211 | Properties:
212 | FunctionName: !Sub securityhub-jira-refresh-${Environment}
213 | Description: Update findings in Security Hub according to JIRA changes
214 | Handler: "sync_securityhub.lambda_handler"
215 | Role:
216 | Fn::GetAtt:
217 | - LambdaRefreshRole
218 | - Arn
219 | Runtime: python3.11
220 | Timeout: 300
221 | Code: ../../dist/lambda.zip
222 | Environment:
223 | Variables:
224 | JIRA_API_TOKEN: !Ref JIRAAPITokenSecret
225 | JIRA_INSTANCE: !Ref JIRAInstance
226 | JIRA_ISSUETYPE: !Ref JIRAIssueType
227 | JIRA_PROJECT_KEY: !Ref JIRAProjectKey
228 |
229 | AlarmSNSTopic:
230 | Type: AWS::SNS::Topic
231 | Properties:
232 | KmsMasterKeyId: alias/aws/sns
233 | TopicName: !Sub "securityhub-jira-alarm-topic-${Environment}"
234 |
235 | CloudWatchAlarmImport:
236 | Type: "AWS::CloudWatch::Alarm"
237 | Properties:
238 | AlarmDescription: "Lambda Critical Error Alarm for Security Hub -> JIRA integration"
239 | ActionsEnabled: true
240 | AlarmActions:
241 | - !Ref AlarmSNSTopic
242 | MetricName: "Errors"
243 | Namespace: "AWS/Lambda"
244 | Statistic: "Sum"
245 | Dimensions:
246 | - Name: "FunctionName"
247 | Value: !Ref JIRASecHubFunction
248 | Period: 300
249 | EvaluationPeriods: 1
250 | DatapointsToAlarm: 1
251 | Threshold: 1
252 | ComparisonOperator: "GreaterThanThreshold"
253 | TreatMissingData: "notBreaching"
254 |
255 | CloudWatchAlarmRefresh:
256 | Type: "AWS::CloudWatch::Alarm"
257 | Properties:
258 | AlarmDescription: "Lambda Critical Error Alarm for JIRA -> Security Hub integration"
259 | ActionsEnabled: true
260 | AlarmActions:
261 | - !Ref AlarmSNSTopic
262 | MetricName: "Errors"
263 | Namespace: "AWS/Lambda"
264 | Statistic: "Sum"
265 | Dimensions:
266 | - Name: "FunctionName"
267 | Value: !Ref RefreshJIRASecHubFunction
268 | Period: 300
269 | EvaluationPeriods: 1
270 | DatapointsToAlarm: 1
271 | Threshold: 1
272 | ComparisonOperator: "GreaterThanThreshold"
273 | TreatMissingData: "notBreaching"
274 |
--------------------------------------------------------------------------------
/src/template/lpt-basic.yaml:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | AWSTemplateFormatVersion: '2010-09-09'
5 | Description: Resoures needed by LPT
6 |
7 | Parameters:
8 | BucketName:
9 | Description: Bucket for the artifacts
10 | Type: String
11 | Resources:
12 | LptBucket:
13 | Type: AWS::S3::Bucket
14 | Properties:
15 | AccessControl: Private
16 | BucketName: !Ref BucketName
17 | PublicAccessBlockConfiguration:
18 | BlockPublicAcls: true
19 | BlockPublicPolicy: true
20 | IgnorePublicAcls: true
21 | RestrictPublicBuckets: true
22 | BucketEncryption:
23 | ServerSideEncryptionConfiguration:
24 | - ServerSideEncryptionByDefault:
25 | SSEAlgorithm: AES256
26 | VersioningConfiguration:
27 | Status: Enabled
28 | LifecycleConfiguration:
29 | Rules:
30 | - Status: Enabled
31 | ExpirationInDays: 365
32 | NoncurrentVersionExpirationInDays: 365
33 | Transitions:
34 | - TransitionInDays: 60
35 | StorageClass: GLACIER
36 | - TransitionInDays: 30
37 | StorageClass: STANDARD_IA
38 | NoncurrentVersionTransitions:
39 | - TransitionInDays: 60
40 | StorageClass: GLACIER
41 | - TransitionInDays: 30
42 | StorageClass: STANDARD_IA
43 | Outputs:
44 | LptBucket:
45 | Value: !Ref LptBucket
46 |
--------------------------------------------------------------------------------
/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -eio pipefail
3 |
4 | # Load the config
5 | if [ -z $1 ]; then
6 | echo "Please specify the environment, Example: ./test.sh prod"
7 | exit 1
8 | else
9 | echo "loading params from \"conf/params_${1}.sh\" environment variables"
10 | . conf/params_${1}.sh
11 | fi
12 |
13 | . conf/params_${1}.sh
14 | export JIRA_API_TOKEN='mock_token'
15 | export JIRA_ISSUETYPE='mock_issuetype'
16 |
17 | python3 -m unittest discover src/code
--------------------------------------------------------------------------------
/test/custom_new.template:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0",
3 | "id": "e215f5c7-a866-e0cd-6d11-fc7ecf97e381",
4 | "detail-type": "Security Hub Findings - Custom Action",
5 | "source": "aws.securityhub",
6 | "account": "123456789012",
7 | "time": "2019-04-11T22:06:13Z",
8 | "region": "us-east-1",
9 | "resources": [
10 | "arn:aws:securityhub:us-east-1:123456789012:action/custom/slackMessaging"
11 | ],
12 | "detail": {
13 | "actionName": "CreateJiraIssue",
14 | "actionDescription": "Create Jira Issue",
15 | "findings": [
16 | {
17 | "SchemaVersion": "2018-10-08",
18 | "Id": "arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0/EC2.8/finding/fba5a282-b865-41fe-bc9c-30357a1e87a7",
19 | "ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/securityhub",
20 | "ProductName": "Security Hub",
21 | "CompanyName": "AWS",
22 | "Region": "eu-west-1",
23 | "GeneratorId": "aws-foundational-security-best-practices/v/1.0.0/EC2.8",
24 | "AwsAccountId": "123456789012",
25 | "Types": [
26 | "Software and Configuration Checks/Industry and Regulatory Standards/AWS-Foundational-Security-Best-Practices"
27 | ],
28 | "FirstObservedAt": "2021-02-04T07:57:13.662Z",
29 | "LastObservedAt": "2022-03-13T18:33:41.172Z",
30 | "CreatedAt": "2021-02-04T07:57:13.662Z",
31 | "UpdatedAt": "2022-03-13T18:33:38.017Z",
32 | "Severity": {
33 | "Product": 70,
34 | "Label": "HIGH",
35 | "Normalized": 70,
36 | "Original": "HIGH"
37 | },
38 | "Title": "EC2.8 EC2 instances should use Instance Metadata Service Version 2 (IMDSv2)",
39 | "Description": "This control checks whether your Amazon Elastic Compute Cloud (Amazon EC2) instance metadata version is configured with Instance Metadata Service Version 2 (IMDSv2). The control passes if HttpTokens is set to required for IMDSv2. The control fails if HttpTokens is set to optional.",
40 | "Remediation": {
41 | "Recommendation": {
42 | "Text": "For directions on how to fix this issue, consult the AWS Security Hub Foundational Security Best Practices documentation.",
43 | "Url": "https://docs.aws.amazon.com/console/securityhub/EC2.8/remediation"
44 | }
45 | },
46 | "ProductFields": {
47 | "StandardsArn": "arn:aws:securityhub:::standards/aws-foundational-security-best-practices/v/1.0.0",
48 | "StandardsSubscriptionArn": "arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0",
49 | "ControlId": "EC2.8",
50 | "RecommendationUrl": "https://docs.aws.amazon.com/console/securityhub/EC2.8/remediation",
51 | "RelatedAWSResources:0/name": "securityhub-ec2-imdsv2-check-3604c928",
52 | "RelatedAWSResources:0/type": "AWS::Config::ConfigRule",
53 | "StandardsControlArn": "arn:aws:securityhub:eu-west-1:123456789012:control/aws-foundational-security-best-practices/v/1.0.0/EC2.8",
54 | "aws/securityhub/ProductName": "Security Hub",
55 | "aws/securityhub/CompanyName": "AWS",
56 | "Resources:0/Id": "arn:aws:ec2:eu-west-1:123456789012:instance/i-0000000aaaaaaaaaa",
57 | "aws/securityhub/FindingId": "arn:aws:securityhub:eu-west-1::product/aws/securityhub/arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0/EC2.8/finding/fba5a282-b865-41fe-bc9c-30357a1e87a7"
58 | },
59 | "Resources": [
60 | {
61 | "Type": "AwsEc2Instance",
62 | "Id": "arn:aws:ec2:eu-west-1:123456789012:instance/i-0000000aaaaaaaaaa",
63 | "Partition": "aws",
64 | "Region": "eu-west-1",
65 | "Details": {
66 | "AwsEc2Instance": {
67 | "ImageId": "ami-05df1afb28e4fcaee",
68 | "VpcId": "vpc-aaaaaaaa",
69 | "SubnetId": "subnet-3242d868",
70 | "LaunchedAt": "2021-02-04T07:54:51.000Z",
71 | "NetworkInterfaces": [
72 | {
73 | "NetworkInterfaceId": "eni-bbbbbbbbbbbbbbbbb"
74 | }
75 | ]
76 | }
77 | }
78 | }
79 | ],
80 | "Compliance": {
81 | "Status": "FAILED"
82 | },
83 | "WorkflowState": "NEW",
84 | "Workflow": {
85 | "Status": "NEW"
86 | },
87 | "RecordState": "ACTIVE",
88 | "FindingProviderFields": {
89 | "Severity": {
90 | "Label": "HIGH",
91 | "Original": "HIGH"
92 | },
93 | "Types": [
94 | "Software and Configuration Checks/Industry and Regulatory Standards/AWS-Foundational-Security-Best-Practices"
95 | ]
96 | }
97 | }
98 | ]
99 | }
100 | }
--------------------------------------------------------------------------------
/test/custom_notified_existing.template:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0",
3 | "id": "e215f5c7-a866-e0cd-6d11-fc7ecf97e381",
4 | "detail-type": "Security Hub Findings - Custom Action",
5 | "source": "aws.securityhub",
6 | "account": "123456789012",
7 | "time": "2019-04-11T22:06:13Z",
8 | "region": "us-east-1",
9 | "resources": [
10 | "arn:aws:securityhub:us-east-1:123456789012:action/custom/slackMessaging"
11 | ],
12 | "detail": {
13 | "actionName": "CreateJiraIssue",
14 | "actionDescription": "Create Jira Issue",
15 | "findings": [
16 | {
17 | "SchemaVersion": "2018-10-08",
18 | "Id": "arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0/EC2.8/finding/fba5a282-b865-41fe-bc9c-30357a1e87a7",
19 | "ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/securityhub",
20 | "ProductName": "Security Hub",
21 | "CompanyName": "AWS",
22 | "Region": "eu-west-1",
23 | "GeneratorId": "aws-foundational-security-best-practices/v/1.0.0/EC2.8",
24 | "AwsAccountId": "123456789012",
25 | "Types": [
26 | "Software and Configuration Checks/Industry and Regulatory Standards/AWS-Foundational-Security-Best-Practices"
27 | ],
28 | "FirstObservedAt": "2021-02-04T07:57:13.662Z",
29 | "LastObservedAt": "2022-03-13T18:33:41.172Z",
30 | "CreatedAt": "2021-02-04T07:57:13.662Z",
31 | "UpdatedAt": "2022-03-13T18:33:38.017Z",
32 | "Severity": {
33 | "Product": 70,
34 | "Label": "HIGH",
35 | "Normalized": 70,
36 | "Original": "HIGH"
37 | },
38 | "Title": "EC2.8 EC2 instances should use Instance Metadata Service Version 2 (IMDSv2)",
39 | "Description": "This control checks whether your Amazon Elastic Compute Cloud (Amazon EC2) instance metadata version is configured with Instance Metadata Service Version 2 (IMDSv2). The control passes if HttpTokens is set to required for IMDSv2. The control fails if HttpTokens is set to optional.",
40 | "Remediation": {
41 | "Recommendation": {
42 | "Text": "For directions on how to fix this issue, consult the AWS Security Hub Foundational Security Best Practices documentation.",
43 | "Url": "https://docs.aws.amazon.com/console/securityhub/EC2.8/remediation"
44 | }
45 | },
46 | "ProductFields": {
47 | "StandardsArn": "arn:aws:securityhub:::standards/aws-foundational-security-best-practices/v/1.0.0",
48 | "StandardsSubscriptionArn": "arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0",
49 | "ControlId": "EC2.8",
50 | "RecommendationUrl": "https://docs.aws.amazon.com/console/securityhub/EC2.8/remediation",
51 | "RelatedAWSResources:0/name": "securityhub-ec2-imdsv2-check-3604c928",
52 | "RelatedAWSResources:0/type": "AWS::Config::ConfigRule",
53 | "StandardsControlArn": "arn:aws:securityhub:eu-west-1:123456789012:control/aws-foundational-security-best-practices/v/1.0.0/EC2.8",
54 | "aws/securityhub/ProductName": "Security Hub",
55 | "aws/securityhub/CompanyName": "AWS",
56 | "Resources:0/Id": "arn:aws:ec2:eu-west-1:123456789012:instance/i-0000000aaaaaaaaaa",
57 | "aws/securityhub/FindingId": "arn:aws:securityhub:eu-west-1::product/aws/securityhub/arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0/EC2.8/finding/fba5a282-b865-41fe-bc9c-30357a1e87a7"
58 | },
59 | "Resources": [
60 | {
61 | "Type": "AwsEc2Instance",
62 | "Id": "arn:aws:ec2:eu-west-1:123456789012:instance/i-0000000aaaaaaaaaa",
63 | "Partition": "aws",
64 | "Region": "eu-west-1",
65 | "Details": {
66 | "AwsEc2Instance": {
67 | "ImageId": "ami-05df1afb28e4fcaee",
68 | "VpcId": "vpc-aaaaaaaa",
69 | "SubnetId": "subnet-3242d868",
70 | "LaunchedAt": "2021-02-04T07:54:51.000Z",
71 | "NetworkInterfaces": [
72 | {
73 | "NetworkInterfaceId": "eni-bbbbbbbbbbbbbbbbb"
74 | }
75 | ]
76 | }
77 | }
78 | }
79 | ],
80 | "Compliance": {
81 | "Status": "FAILED"
82 | },
83 | "WorkflowState": "NOTIFIED",
84 | "Workflow": {
85 | "Status": "NOTIFIED"
86 | },
87 | "RecordState": "ACTIVE",
88 | "FindingProviderFields": {
89 | "Severity": {
90 | "Label": "HIGH",
91 | "Original": "HIGH"
92 | },
93 | "Types": [
94 | "Software and Configuration Checks/Industry and Regulatory Standards/AWS-Foundational-Security-Best-Practices"
95 | ]
96 | }
97 | }
98 | ]
99 | }
100 | }
--------------------------------------------------------------------------------
/test/imported_archived_existing.template:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0",
3 | "id": "8e5622f9-d81c-4d81-612a-9319e7ee2506",
4 | "detail-type": "Security Hub Findings - Imported",
5 | "source": "aws.securityhub",
6 | "account": "123456789012",
7 | "time": "2019-04-11T21:52:17Z",
8 | "region": "eu-west-1",
9 | "resources": [
10 | "arn:aws:securityhub:eu-west-1::product/aws/macie/arn:aws:macie:us-west-2:123456789012:integtest/trigger/6294d71b927c41cbab915159a8f326a3/alert/f2893b211841"
11 | ],
12 | "detail": {
13 | "findings": [
14 | {
15 | "SchemaVersion": "2018-10-08",
16 | "Id": "arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0/EC2.8/finding/fba5a282-b865-41fe-bc9c-30357a1e87a7",
17 | "ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/securityhub",
18 | "ProductName": "Security Hub",
19 | "CompanyName": "AWS",
20 | "Region": "eu-west-1",
21 | "GeneratorId": "aws-foundational-security-best-practices/v/1.0.0/EC2.8",
22 | "AwsAccountId": "123456789012",
23 | "Types": [
24 | "Software and Configuration Checks/Industry and Regulatory Standards/AWS-Foundational-Security-Best-Practices"
25 | ],
26 | "FirstObservedAt": "2021-02-04T07:57:13.662Z",
27 | "LastObservedAt": "2022-03-13T18:33:41.172Z",
28 | "CreatedAt": "2021-02-04T07:57:13.662Z",
29 | "UpdatedAt": "2022-03-13T18:33:38.017Z",
30 | "Severity": {
31 | "Product": 70,
32 | "Label": "HIGH",
33 | "Normalized": 70,
34 | "Original": "HIGH"
35 | },
36 | "Title": "EC2.8 EC2 instances should use Instance Metadata Service Version 2 (IMDSv2)",
37 | "Description": "This control checks whether your Amazon Elastic Compute Cloud (Amazon EC2) instance metadata version is configured with Instance Metadata Service Version 2 (IMDSv2). The control passes if HttpTokens is set to required for IMDSv2. The control fails if HttpTokens is set to optional.",
38 | "Remediation": {
39 | "Recommendation": {
40 | "Text": "For directions on how to fix this issue, consult the AWS Security Hub Foundational Security Best Practices documentation.",
41 | "Url": "https://docs.aws.amazon.com/console/securityhub/EC2.8/remediation"
42 | }
43 | },
44 | "ProductFields": {
45 | "StandardsArn": "arn:aws:securityhub:::standards/aws-foundational-security-best-practices/v/1.0.0",
46 | "StandardsSubscriptionArn": "arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0",
47 | "ControlId": "EC2.8",
48 | "RecommendationUrl": "https://docs.aws.amazon.com/console/securityhub/EC2.8/remediation",
49 | "RelatedAWSResources:0/name": "securityhub-ec2-imdsv2-check-3604c928",
50 | "RelatedAWSResources:0/type": "AWS::Config::ConfigRule",
51 | "StandardsControlArn": "arn:aws:securityhub:eu-west-1:123456789012:control/aws-foundational-security-best-practices/v/1.0.0/EC2.8",
52 | "aws/securityhub/ProductName": "Security Hub",
53 | "aws/securityhub/CompanyName": "AWS",
54 | "Resources:0/Id": "arn:aws:ec2:eu-west-1:123456789012:instance/i-0000000aaaaaaaaaa",
55 | "aws/securityhub/FindingId": "arn:aws:securityhub:eu-west-1::product/aws/securityhub/arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0/EC2.8/finding/fba5a282-b865-41fe-bc9c-30357a1e87a7"
56 | },
57 | "Resources": [
58 | {
59 | "Type": "AwsEc2Instance",
60 | "Id": "arn:aws:ec2:eu-west-1:123456789012:instance/i-0000000aaaaaaaaaa",
61 | "Partition": "aws",
62 | "Region": "eu-west-1",
63 | "Details": {
64 | "AwsEc2Instance": {
65 | "ImageId": "ami-05df1afb28e4fcaee",
66 | "VpcId": "vpc-aaaaaaaa",
67 | "SubnetId": "subnet-3242d868",
68 | "LaunchedAt": "2021-02-04T07:54:51.000Z",
69 | "NetworkInterfaces": [
70 | {
71 | "NetworkInterfaceId": "eni-bbbbbbbbbbbbbbbbb"
72 | }
73 | ]
74 | }
75 | }
76 | }
77 | ],
78 | "FindingProviderFields": {
79 | "Severity": {
80 | "Label": "HIGH",
81 | "Original": "HIGH"
82 | },
83 | "Types": [
84 | "Software and Configuration Checks/Industry and Regulatory Standards/AWS-Foundational-Security-Best-Practices"
85 | ]
86 | },
87 | "Compliance": {
88 | "Status": "FAILED"
89 | },
90 | "WorkflowState": "NOTIFIED",
91 | "Workflow": {
92 | "Status": "NOTIFIED"
93 | },
94 | "RecordState": "ARCHIVED"
95 | }
96 | ]
97 | }
98 | }
--------------------------------------------------------------------------------
/test/imported_archived_new.template:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0",
3 | "id": "8e5622f9-d81c-4d81-612a-9319e7ee2506",
4 | "detail-type": "Security Hub Findings - Imported",
5 | "source": "aws.securityhub",
6 | "account": "123456789012",
7 | "time": "2019-04-11T21:52:17Z",
8 | "region": "eu-west-1",
9 | "resources": [
10 | "arn:aws:securityhub:eu-west-1::product/aws/macie/arn:aws:macie:us-west-2:123456789012:integtest/trigger/6294d71b927c41cbab915159a8f326a3/alert/f2893b211841"
11 | ],
12 | "detail": {
13 | "findings": [
14 | {
15 | "SchemaVersion": "2018-10-08",
16 | "Id": "arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0/EC2.8/finding/fba5a282-b865-41fe-bc9c-30357a1e87a7",
17 | "ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/securityhub",
18 | "ProductName": "Security Hub",
19 | "CompanyName": "AWS",
20 | "Region": "eu-west-1",
21 | "GeneratorId": "aws-foundational-security-best-practices/v/1.0.0/EC2.8",
22 | "AwsAccountId": "123456789012",
23 | "Types": [
24 | "Software and Configuration Checks/Industry and Regulatory Standards/AWS-Foundational-Security-Best-Practices"
25 | ],
26 | "FirstObservedAt": "2021-02-04T07:57:13.662Z",
27 | "LastObservedAt": "2022-03-13T18:33:41.172Z",
28 | "CreatedAt": "2021-02-04T07:57:13.662Z",
29 | "UpdatedAt": "2022-03-13T18:33:38.017Z",
30 | "Severity": {
31 | "Product": 70,
32 | "Label": "HIGH",
33 | "Normalized": 70,
34 | "Original": "HIGH"
35 | },
36 | "Title": "EC2.8 EC2 instances should use Instance Metadata Service Version 2 (IMDSv2)",
37 | "Description": "This control checks whether your Amazon Elastic Compute Cloud (Amazon EC2) instance metadata version is configured with Instance Metadata Service Version 2 (IMDSv2). The control passes if HttpTokens is set to required for IMDSv2. The control fails if HttpTokens is set to optional.",
38 | "Remediation": {
39 | "Recommendation": {
40 | "Text": "For directions on how to fix this issue, consult the AWS Security Hub Foundational Security Best Practices documentation.",
41 | "Url": "https://docs.aws.amazon.com/console/securityhub/EC2.8/remediation"
42 | }
43 | },
44 | "ProductFields": {
45 | "StandardsArn": "arn:aws:securityhub:::standards/aws-foundational-security-best-practices/v/1.0.0",
46 | "StandardsSubscriptionArn": "arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0",
47 | "ControlId": "EC2.8",
48 | "RecommendationUrl": "https://docs.aws.amazon.com/console/securityhub/EC2.8/remediation",
49 | "RelatedAWSResources:0/name": "securityhub-ec2-imdsv2-check-3604c928",
50 | "RelatedAWSResources:0/type": "AWS::Config::ConfigRule",
51 | "StandardsControlArn": "arn:aws:securityhub:eu-west-1:123456789012:control/aws-foundational-security-best-practices/v/1.0.0/EC2.8",
52 | "aws/securityhub/ProductName": "Security Hub",
53 | "aws/securityhub/CompanyName": "AWS",
54 | "Resources:0/Id": "arn:aws:ec2:eu-west-1:123456789012:instance/i-0000000aaaaaaaaaa",
55 | "aws/securityhub/FindingId": "arn:aws:securityhub:eu-west-1::product/aws/securityhub/arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0/EC2.8/finding/fba5a282-b865-41fe-bc9c-30357a1e87a7"
56 | },
57 | "Resources": [
58 | {
59 | "Type": "AwsEc2Instance",
60 | "Id": "arn:aws:ec2:eu-west-1:123456789012:instance/i-0000000aaaaaaaaaa",
61 | "Partition": "aws",
62 | "Region": "eu-west-1",
63 | "Details": {
64 | "AwsEc2Instance": {
65 | "ImageId": "ami-05df1afb28e4fcaee",
66 | "VpcId": "vpc-aaaaaaaa",
67 | "SubnetId": "subnet-3242d868",
68 | "LaunchedAt": "2021-02-04T07:54:51.000Z",
69 | "NetworkInterfaces": [
70 | {
71 | "NetworkInterfaceId": "eni-bbbbbbbbbbbbbbbbb"
72 | }
73 | ]
74 | }
75 | }
76 | }
77 | ],
78 | "FindingProviderFields": {
79 | "Severity": {
80 | "Label": "HIGH",
81 | "Original": "HIGH"
82 | },
83 | "Types": [
84 | "Software and Configuration Checks/Industry and Regulatory Standards/AWS-Foundational-Security-Best-Practices"
85 | ]
86 | },
87 | "Compliance": {
88 | "Status": "FAILED"
89 | },
90 | "WorkflowState": "NEW",
91 | "Workflow": {
92 | "Status": "NEW"
93 | },
94 | "RecordState": "ARCHIVED"
95 | }
96 | ]
97 | }
98 | }
--------------------------------------------------------------------------------
/test/imported_archived_notified.template:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0",
3 | "id": "8e5622f9-d81c-4d81-612a-9319e7ee2506",
4 | "detail-type": "Security Hub Findings - Imported",
5 | "source": "aws.securityhub",
6 | "account": "123456789012",
7 | "time": "2019-04-11T21:52:17Z",
8 | "region": "eu-west-1",
9 | "resources": [
10 | "arn:aws:securityhub:eu-west-1::product/aws/macie/arn:aws:macie:us-west-2:123456789012:integtest/trigger/6294d71b927c41cbab915159a8f326a3/alert/f2893b211841"
11 | ],
12 | "detail": {
13 | "findings": [
14 | {
15 | "SchemaVersion": "2018-10-08",
16 | "Id": "arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0/EC2.8/finding/fba5a282-b865-41fe-bc9c-30357a1e87a7",
17 | "ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/securityhub",
18 | "ProductName": "Security Hub",
19 | "CompanyName": "AWS",
20 | "Region": "eu-west-1",
21 | "GeneratorId": "aws-foundational-security-best-practices/v/1.0.0/EC2.8",
22 | "AwsAccountId": "123456789012",
23 | "Types": [
24 | "Software and Configuration Checks/Industry and Regulatory Standards/AWS-Foundational-Security-Best-Practices"
25 | ],
26 | "FirstObservedAt": "2021-02-04T07:57:13.662Z",
27 | "LastObservedAt": "2022-03-13T18:33:41.172Z",
28 | "CreatedAt": "2021-02-04T07:57:13.662Z",
29 | "UpdatedAt": "2022-03-13T18:33:38.017Z",
30 | "Severity": {
31 | "Product": 70,
32 | "Label": "HIGH",
33 | "Normalized": 70,
34 | "Original": "HIGH"
35 | },
36 | "Title": "EC2.8 EC2 instances should use Instance Metadata Service Version 2 (IMDSv2)",
37 | "Description": "This control checks whether your Amazon Elastic Compute Cloud (Amazon EC2) instance metadata version is configured with Instance Metadata Service Version 2 (IMDSv2). The control passes if HttpTokens is set to required for IMDSv2. The control fails if HttpTokens is set to optional.",
38 | "Remediation": {
39 | "Recommendation": {
40 | "Text": "For directions on how to fix this issue, consult the AWS Security Hub Foundational Security Best Practices documentation.",
41 | "Url": "https://docs.aws.amazon.com/console/securityhub/EC2.8/remediation"
42 | }
43 | },
44 | "ProductFields": {
45 | "StandardsArn": "arn:aws:securityhub:::standards/aws-foundational-security-best-practices/v/1.0.0",
46 | "StandardsSubscriptionArn": "arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0",
47 | "ControlId": "EC2.8",
48 | "RecommendationUrl": "https://docs.aws.amazon.com/console/securityhub/EC2.8/remediation",
49 | "RelatedAWSResources:0/name": "securityhub-ec2-imdsv2-check-3604c928",
50 | "RelatedAWSResources:0/type": "AWS::Config::ConfigRule",
51 | "StandardsControlArn": "arn:aws:securityhub:eu-west-1:123456789012:control/aws-foundational-security-best-practices/v/1.0.0/EC2.8",
52 | "aws/securityhub/ProductName": "Security Hub",
53 | "aws/securityhub/CompanyName": "AWS",
54 | "Resources:0/Id": "arn:aws:ec2:eu-west-1:123456789012:instance/i-0000000aaaaaaaaaa",
55 | "aws/securityhub/FindingId": "arn:aws:securityhub:eu-west-1::product/aws/securityhub/arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0/EC2.8/finding/fba5a282-b865-41fe-bc9c-30357a1e87a7"
56 | },
57 | "Resources": [
58 | {
59 | "Type": "AwsEc2Instance",
60 | "Id": "arn:aws:ec2:eu-west-1:123456789012:instance/i-0000000aaaaaaaaaa",
61 | "Partition": "aws",
62 | "Region": "eu-west-1",
63 | "Details": {
64 | "AwsEc2Instance": {
65 | "ImageId": "ami-05df1afb28e4fcaee",
66 | "VpcId": "vpc-aaaaaaaa",
67 | "SubnetId": "subnet-3242d868",
68 | "LaunchedAt": "2021-02-04T07:54:51.000Z",
69 | "NetworkInterfaces": [
70 | {
71 | "NetworkInterfaceId": "eni-bbbbbbbbbbbbbbbbb"
72 | }
73 | ]
74 | }
75 | }
76 | }
77 | ],
78 | "FindingProviderFields": {
79 | "Severity": {
80 | "Label": "HIGH",
81 | "Original": "HIGH"
82 | },
83 | "Types": [
84 | "Software and Configuration Checks/Industry and Regulatory Standards/AWS-Foundational-Security-Best-Practices"
85 | ]
86 | },
87 | "Compliance": {
88 | "Status": "FAILED"
89 | },
90 | "WorkflowState": "NOTIFIED",
91 | "Workflow": {
92 | "Status": "NOTIFIED"
93 | },
94 | "RecordState": "ARCHIVED"
95 | }
96 | ]
97 | }
98 | }
--------------------------------------------------------------------------------
/test/imported_new_automated_standard_check.template:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0",
3 | "id": "8e5622f9-d81c-4d81-612a-9319e7ee2506",
4 | "detail-type": "Security Hub Findings - Imported",
5 | "source": "aws.securityhub",
6 | "account": "123456789012",
7 | "time": "2019-04-11T21:52:17Z",
8 | "region": "eu-west-1",
9 | "resources": [
10 | "arn:aws:securityhub:eu-west-1::product/aws/macie/arn:aws:macie:us-west-2:123456789012:integtest/trigger/6294d71b927c41cbab915159a8f326a3/alert/f2893b211841"
11 | ],
12 | "detail": {
13 | "findings": [
14 | {
15 | "SchemaVersion": "2018-10-08",
16 | "Id": "arn:aws:securityhub:eu-west-1:123456789012:subscription/cis-aws-foundations-benchmark/v/1.2.0/1.22/finding/dedc1e61-e4e6-448e-87d3-c7d1709334a3",
17 | "ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/securityhub",
18 | "GeneratorId": "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0/rule/1.22",
19 | "AwsAccountId": "123456789012",
20 | "Types": [
21 | "Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
22 | ],
23 | "FirstObservedAt": "2020-07-22T17:12:27.005Z",
24 | "LastObservedAt": "2020-11-10T21:07:43.968Z",
25 | "CreatedAt": "2020-07-22T17:12:27.005Z",
26 | "UpdatedAt": "2020-11-10T21:07:43.287Z",
27 | "Severity": {
28 | "Product": 70,
29 | "Label": "HIGH",
30 | "Normalized": 70,
31 | "Original": "HIGH"
32 | },
33 | "Title": "1.22 Ensure IAM policies that allow full \"*:*\" administrative privileges are not created",
34 | "Description": "IAM policies are the means by which privileges are granted to users, groups, or roles. It is recommended and considered a standard security advice to grant least privilege—that is, granting only the permissions required to perform a task. Determine what users need to do and then craft policies for them that let the users perform only those tasks, instead of allowing full administrative privileges.",
35 | "Remediation": {
36 | "Recommendation": {
37 | "Text": "For directions on how to fix this issue, please consult the AWS Security Hub CIS documentation.",
38 | "Url": "https://docs.aws.amazon.com/console/securityhub/standards-cis-1.22/remediation"
39 | }
40 | },
41 | "ProductFields": {
42 | "StandardsGuideArn": "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0",
43 | "StandardsGuideSubscriptionArn": "arn:aws:securityhub:eu-west-1:123456789012:subscription/cis-aws-foundations-benchmark/v/1.2.0",
44 | "RuleId": "1.22",
45 | "RecommendationUrl": "https://docs.aws.amazon.com/console/securityhub/standards-cis-1.22/remediation",
46 | "RelatedAWSResources:0/name": "securityhub-iam-policy-no-statements-with-admin-access-dcb0d040",
47 | "RelatedAWSResources:0/type": "AWS::Config::ConfigRule",
48 | "StandardsControlArn": "arn:aws:securityhub:eu-west-1:123456789012:control/cis-aws-foundations-benchmark/v/1.2.0/1.22",
49 | "aws/securityhub/ProductName": "Security Hub",
50 | "aws/securityhub/CompanyName": "AWS",
51 | "aws/securityhub/FindingId": "arn:aws:securityhub:eu-west-1::product/aws/securityhub/arn:aws:securityhub:eu-west-1:123456789012:subscription/cis-aws-foundations-benchmark/v/1.2.0/1.22/finding/dedc1e61-e4e6-448e-87d3-c7d1709334a3"
52 | },
53 | "Resources": [
54 | {
55 | "Type": "AwsIamPolicy",
56 | "Id": "arn:aws:iam::123456789012:policy/AdminAccessCustom",
57 | "Partition": "aws",
58 | "Region": "eu-west-1",
59 | "Details": {
60 | "AwsIamPolicy": {
61 | "AttachmentCount": 1,
62 | "CreateDate": "2020-07-22T17:00:49.000Z",
63 | "DefaultVersionId": "v1",
64 | "IsAttachable": true,
65 | "Path": "/",
66 | "PermissionsBoundaryUsageCount": 0,
67 | "PolicyId": "ANPA6CQ2O7EC27CMBJAJF",
68 | "PolicyName": "AdminAccessCustom",
69 | "PolicyVersionList": [
70 | {
71 | "VersionId": "v1",
72 | "IsDefaultVersion": true,
73 | "CreateDate": "2020-07-22T17:00:49.000Z"
74 | }
75 | ],
76 | "UpdateDate": "2020-07-22T17:00:49.000Z"
77 | }
78 | }
79 | }
80 | ],
81 | "Compliance": {
82 | "Status": "FAILED"
83 | },
84 | "WorkflowState": "NEW",
85 | "Workflow": {
86 | "Status": "NEW"
87 | },
88 | "RecordState": "ACTIVE"
89 | }
90 | ]
91 | }
92 | }
--------------------------------------------------------------------------------
/test/imported_new_notautomated.template:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0",
3 | "id": "8e5622f9-d81c-4d81-612a-9319e7ee2506",
4 | "detail-type": "Security Hub Findings - Imported",
5 | "source": "aws.securityhub",
6 | "account": "123456789012",
7 | "time": "2019-04-11T21:52:17Z",
8 | "region": "eu-west-1",
9 | "resources": [
10 | "arn:aws:securityhub:eu-west-1::product/aws/macie/arn:aws:macie:us-west-2:123456789012:integtest/trigger/6294d71b927c41cbab915159a8f326a3/alert/f2893b211841"
11 | ],
12 | "detail": {
13 | "findings": [
14 | {
15 | "SchemaVersion": "2018-10-08",
16 | "Id": "arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0/EC2.8/finding/fba5a282-b865-41fe-bc9c-30357a1e87a7",
17 | "ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/securityhub",
18 | "ProductName": "Security Hub",
19 | "CompanyName": "AWS",
20 | "Region": "eu-west-1",
21 | "GeneratorId": "aws-foundational-security-best-practices/v/1.0.0/EC2.8",
22 | "AwsAccountId": "123456789012",
23 | "Types": [
24 | "Software and Configuration Checks/Industry and Regulatory Standards/AWS-Foundational-Security-Best-Practices"
25 | ],
26 | "FirstObservedAt": "2021-02-04T07:57:13.662Z",
27 | "LastObservedAt": "2022-03-13T18:33:41.172Z",
28 | "CreatedAt": "2021-02-04T07:57:13.662Z",
29 | "UpdatedAt": "2022-03-13T18:33:38.017Z",
30 | "Severity": {
31 | "Product": 70,
32 | "Label": "HIGH",
33 | "Normalized": 70,
34 | "Original": "HIGH"
35 | },
36 | "Title": "EC2.8 EC2 instances should use Instance Metadata Service Version 2 (IMDSv2)",
37 | "Description": "This control checks whether your Amazon Elastic Compute Cloud (Amazon EC2) instance metadata version is configured with Instance Metadata Service Version 2 (IMDSv2). The control passes if HttpTokens is set to required for IMDSv2. The control fails if HttpTokens is set to optional.",
38 | "Remediation": {
39 | "Recommendation": {
40 | "Text": "For directions on how to fix this issue, consult the AWS Security Hub Foundational Security Best Practices documentation.",
41 | "Url": "https://docs.aws.amazon.com/console/securityhub/EC2.8/remediation"
42 | }
43 | },
44 | "ProductFields": {
45 | "StandardsArn": "arn:aws:securityhub:::standards/aws-foundational-security-best-practices/v/1.0.0",
46 | "StandardsSubscriptionArn": "arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0",
47 | "ControlId": "EC2.8",
48 | "RecommendationUrl": "https://docs.aws.amazon.com/console/securityhub/EC2.8/remediation",
49 | "RelatedAWSResources:0/name": "securityhub-ec2-imdsv2-check-3604c928",
50 | "RelatedAWSResources:0/type": "AWS::Config::ConfigRule",
51 | "StandardsControlArn": "arn:aws:securityhub:eu-west-1:123456789012:control/aws-foundational-security-best-practices/v/1.0.0/EC2.8",
52 | "aws/securityhub/ProductName": "Security Hub",
53 | "aws/securityhub/CompanyName": "AWS",
54 | "Resources:0/Id": "arn:aws:ec2:eu-west-1:123456789012:instance/i-0000000aaaaaaaaaa",
55 | "aws/securityhub/FindingId": "arn:aws:securityhub:eu-west-1::product/aws/securityhub/arn:aws:securityhub:eu-west-1:123456789012:subscription/aws-foundational-security-best-practices/v/1.0.0/EC2.8/finding/fba5a282-b865-41fe-bc9c-30357a1e87a7"
56 | },
57 | "Resources": [
58 | {
59 | "Type": "AwsEc2Instance",
60 | "Id": "arn:aws:ec2:eu-west-1:123456789012:instance/i-0000000aaaaaaaaaa",
61 | "Partition": "aws",
62 | "Region": "eu-west-1",
63 | "Details": {
64 | "AwsEc2Instance": {
65 | "ImageId": "ami-05df1afb28e4fcaee",
66 | "VpcId": "vpc-aaaaaaaa",
67 | "SubnetId": "subnet-3242d868",
68 | "LaunchedAt": "2021-02-04T07:54:51.000Z",
69 | "NetworkInterfaces": [
70 | {
71 | "NetworkInterfaceId": "eni-bbbbbbbbbbbbbbbbb"
72 | }
73 | ]
74 | }
75 | }
76 | }
77 | ],
78 | "FindingProviderFields": {
79 | "Severity": {
80 | "Label": "HIGH",
81 | "Original": "HIGH"
82 | },
83 | "Types": [
84 | "Software and Configuration Checks/Industry and Regulatory Standards/AWS-Foundational-Security-Best-Practices"
85 | ]
86 | },
87 | "Compliance": {
88 | "Status": "FAILED"
89 | },
90 | "WorkflowState": "NEW",
91 | "Workflow": {
92 | "Status": "NEW"
93 | },
94 | "RecordState": "ACTIVE"
95 | }
96 | ]
97 | }
98 | }
--------------------------------------------------------------------------------