├── sample-ruby ├── sample.env ├── Gemfile └── tests.rb ├── sample_tf.zip ├── sample-python ├── legacy │ ├── requirements.txt │ ├── scan_details.py │ ├── scan_details_p3.py │ ├── post_yaml_cloudformation.py │ ├── scan_list_p3_params.py │ ├── post_yaml_cloudformation_python3.py │ ├── post_terraform_folder.py │ ├── realtime_scan.py │ └── realtime_scan_p3.py ├── pyproject.toml ├── helpers │ ├── auth.py │ └── aqua.py ├── main.py ├── readme.md └── poetry.lock ├── codelessPlugin ├── aquaplugin-BucketNaming.json ├── aquaplugin-Volumeattachmentstate.json ├── aquaplugin-DevStages.json ├── aquaplugin-ec2instancearenotofnanotype.json ├── aquaplugin-S3SSEEncryption.json ├── aquaplugin-IAMUsernameMatchesRegexnew.json ├── aquaplugin-KeyRotationenabled.json ├── aquaplugin-APIGatewayPrivateEndpointsnew.json ├── aquaplugin-EKSKubernetesVersionNew.json ├── aquaplugin-S3BucketVersioningNew.json ├── aquaplugin-SNSTopicCMKEncryptionnew.json ├── aquaplugin-ELBv2HTTPSOnlyNew.json ├── aquaplugin-LambdaTracingEnabledNew.json ├── aquaplugin-KMSScheduledDeletionnew.json └── aquaplugin-DynamoDBContinuousBackupsNew.json ├── README.md ├── sample-js ├── make_request.js ├── realtime_scan.js ├── Inventory-AWS.js ├── Inventory-Azure.js └── generate_new_key_aws.js ├── .gitignore ├── sample_cf.yaml └── purposely_insecure_terraform.tf /sample-ruby/sample.env: -------------------------------------------------------------------------------- 1 | API_KEY=api-key-123 2 | API_SECRET=api-secret-456 3 | -------------------------------------------------------------------------------- /sample_tf.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquasecurity/saas-api-samples/HEAD/sample_tf.zip -------------------------------------------------------------------------------- /sample-python/legacy/requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2020.12.5 2 | chardet==4.0.0 3 | idna==2.10 4 | python-dotenv==0.15.0 5 | requests==2.25.1 6 | urllib3==1.26.5 7 | -------------------------------------------------------------------------------- /sample-ruby/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 6 | 7 | gem "http", "~> 5.0" 8 | gem "dotenv", "~> 2.7" 9 | -------------------------------------------------------------------------------- /sample-python/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "compliance_status" 3 | version = "0.0.1" 4 | description = "Sample Aqua CSPM Compliance Status API Integration" 5 | authors = ["Your Name "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | requests = "^2.27.1" 10 | python-dotenv = "^0.19.2" 11 | 12 | [tool.poetry.dev-dependencies] 13 | black = "^22.1.0" 14 | 15 | [build-system] 16 | requires = ["poetry-core>=1.0.0"] 17 | build-backend = "poetry.core.masonry.api" 18 | -------------------------------------------------------------------------------- /sample-ruby/tests.rb: -------------------------------------------------------------------------------- 1 | require 'http' 2 | require 'openssl' 3 | require 'base64' 4 | require 'dotenv/load' 5 | 6 | KEY = ENV['API_KEY'] 7 | SECRET = ENV['API_SECRET'] 8 | 9 | uri = 'https://api.cloudsploit.com' 10 | path = '/v2/tests' 11 | url = "#{uri}#{path}" 12 | timestamp = (Time.now.to_f * 1000).to_i 13 | string = "#{timestamp}GET#{path}" 14 | 15 | signature = OpenSSL::HMAC.hexdigest('SHA256', SECRET, string) 16 | 17 | headers = { 18 | 'Accept': 'application/json', 19 | 'Content-Type': 'application/json', 20 | 'X-API-Key': KEY, 21 | 'X-Signature': signature, 22 | 'X-Timestamp': timestamp 23 | } 24 | 25 | res = HTTP.headers(headers).get(url).body 26 | 27 | puts res.to_s 28 | -------------------------------------------------------------------------------- /codelessPlugin/aquaplugin-BucketNaming.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1042, 3 | "title": "Bucket Naming", 4 | "description": "Bucket Naming starting with Aqua", 5 | "severity": "low", 6 | "more_info": "buckets should be named after company name", 7 | "recommended_action": "create buckets with names starting with aqua", 8 | "link": "http://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html", 9 | "category": "S3", 10 | "asl": { 11 | "apis": [ 12 | "S3:listBuckets" 13 | ], 14 | "conditions": [ 15 | { 16 | "service": "s3", 17 | "api": "listBuckets", 18 | "property": "Name", 19 | "op": "MATCHES", 20 | "value": "^aqua.*$", 21 | "override": false, 22 | "transform": "NONE" 23 | } 24 | ], 25 | "version": 1 26 | } 27 | } -------------------------------------------------------------------------------- /codelessPlugin/aquaplugin-Volumeattachmentstate.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1044, 3 | "title": "Volume attachment state", 4 | "description": "volumes should be attached", 5 | "severity": "low", 6 | "more_info": "Find out unattached volumes", 7 | "recommended_action": "Remove un-attached volumes", 8 | "link": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volumes.html", 9 | "category": "EC2", 10 | "asl": { 11 | "apis": [ 12 | "EC2:describeVolumes" 13 | ], 14 | "conditions": [ 15 | { 16 | "service": "ec2", 17 | "api": "describeVolumes", 18 | "property": "Attachments[*].State", 19 | "op": "NE", 20 | "value": "attached", 21 | "override": false, 22 | "transform": "NONE" 23 | } 24 | ], 25 | "version": 1 26 | } 27 | } -------------------------------------------------------------------------------- /codelessPlugin/aquaplugin-DevStages.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1043, 3 | "title": "Dev Stages", 4 | "description": "in prod enviornment there should not be stages called dev", 5 | "severity": "low", 6 | "more_info": "There should not be dev stage in prod env", 7 | "recommended_action": "create stage with named after prod", 8 | "link": "https://aws.amazon.com/api-gateway/", 9 | "category": "API Gateway", 10 | "asl": { 11 | "apis": [ 12 | "APIGateway:getStages", 13 | "APIGateway:getRestApis" 14 | ], 15 | "conditions": [ 16 | { 17 | "service": "apigateway", 18 | "api": "getStages", 19 | "property": "item[*].stageName", 20 | "op": "EQ", 21 | "value": "dev", 22 | "override": false, 23 | "transform": "NONE" 24 | } 25 | ], 26 | "version": 1 27 | } 28 | } -------------------------------------------------------------------------------- /codelessPlugin/aquaplugin-ec2instancearenotofnanotype.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1039, 3 | "title": "ec2 instance are not of nano type", 4 | "description": "detect if instances are of nano size", 5 | "severity": "low", 6 | "more_info": "this plugin checks that instance type should not be t2.nano", 7 | "recommended_action": "upgrade the instance to be above than nano", 8 | "link": "https://aws.amazon.com/ec2/instance-types/", 9 | "category": "EC2", 10 | "asl": { 11 | "apis": [ 12 | "EC2:describeInstances" 13 | ], 14 | "conditions": [ 15 | { 16 | "service": "ec2", 17 | "api": "describeInstances", 18 | "property": "Instances[*].InstanceType", 19 | "op": "NE", 20 | "value": "t2.nano", 21 | "override": false, 22 | "transform": "STRING" 23 | } 24 | ], 25 | "version": 1 26 | } 27 | } -------------------------------------------------------------------------------- /sample-python/helpers/auth.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import hmac 4 | import hashlib 5 | from urllib.parse import urlparse 6 | 7 | api_key = os.getenv("AQUA_API_KEY") 8 | api_secret = os.getenv("AQUA_API_SECRET") 9 | 10 | 11 | def headers(url: str, method: str = "GET") -> dict: 12 | timestamp = str(int(time.time() * 1000)) 13 | path = urlparse(url).path 14 | string = timestamp + method + path 15 | 16 | secret_bytes = bytes(api_secret, "utf-8") 17 | string_bytes = bytes(string, "utf-8") 18 | 19 | sig = hmac.new(secret_bytes, msg=string_bytes, digestmod=hashlib.sha256).hexdigest() 20 | 21 | headers = { 22 | "accept": "application/json", 23 | "x-api-key": api_key, 24 | "x-signature": sig, 25 | "x-timestamp": timestamp, 26 | "content-type": "application/json", 27 | } 28 | 29 | return headers 30 | -------------------------------------------------------------------------------- /codelessPlugin/aquaplugin-S3SSEEncryption.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 985, 3 | "title": "S3 SSE Encryption", 4 | "description": "Ensures SSE encryption is enabled on S3 buckets", 5 | "severity": "low", 6 | "more_info": "'S3 object encryption provides fully-managed encryption of all objects uploaded to an S3 bucket.',", 7 | "recommended_action": "Enable SSE CMK KMS-based encryption for all S3 buckets.'", 8 | "link": "https://docs.aws.amazon.com/AmazonS3/latest/dev/bucket-encryption.html", 9 | "category": "S3", 10 | "asl": { 11 | "apis": [ 12 | "S3:getBucketEncryption", 13 | "S3:listBuckets" 14 | ], 15 | "version": 1, 16 | "conditions": [ 17 | { 18 | "service": "s3", 19 | "api": "getBucketEncryption", 20 | "property": "ServerSideEncryptionConfiguration", 21 | "op": "EXISTS", 22 | "value": "", 23 | "override": false, 24 | "transform": "NONE" 25 | } 26 | ] 27 | } 28 | } -------------------------------------------------------------------------------- /codelessPlugin/aquaplugin-IAMUsernameMatchesRegexnew.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 989, 3 | "title": "IAM Username Matches Regex new", 4 | "description": "Ensures all IAM user names match the given regex", 5 | "severity": "low", 6 | "more_info": "Many organizational policies require IAM user names to follow a common naming convention. This check ensures these conventions are followed.", 7 | "recommended_action": "Rename the IAM user name to match the provided regex.", 8 | "link": "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html", 9 | "category": "IAM", 10 | "asl": { 11 | "apis": [ 12 | "IAM:generateCredentialReport" 13 | ], 14 | "conditions": [ 15 | { 16 | "service": "iam", 17 | "api": "generateCredentialReport", 18 | "property": "user", 19 | "op": "MATCHES", 20 | "value": "^.*$", 21 | "override": false, 22 | "transform": "STRING" 23 | } 24 | ], 25 | "version": 1 26 | } 27 | } -------------------------------------------------------------------------------- /codelessPlugin/aquaplugin-KeyRotationenabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1045, 3 | "title": "Key Rotation enabled", 4 | "description": "Key rotation should be enabled", 5 | "severity": "low", 6 | "more_info": "When you enable automatic key rotation for a customer managed CMK, AWS KMS generates new cryptographic material for the CMK every year", 7 | "recommended_action": "Enable automatic key rotation for a customer managed CMK,", 8 | "link": "https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html", 9 | "category": "KMS", 10 | "asl": { 11 | "apis": [ 12 | "KMS:getKeyRotationStatus", 13 | "KMS:listKeys" 14 | ], 15 | "version": 1, 16 | "conditions": [ 17 | { 18 | "service": "kms", 19 | "api": "getKeyRotationStatus", 20 | "property": "KeyRotationEnabled", 21 | "op": "ISTRUE", 22 | "value": "", 23 | "override": false, 24 | "transform": "NONE" 25 | } 26 | ] 27 | } 28 | } -------------------------------------------------------------------------------- /sample-python/legacy/scan_details.py: -------------------------------------------------------------------------------- 1 | # This Python script will query for the details 2 | # of a scan given a scan ID (with appropriate access) 3 | 4 | import json 5 | import time 6 | import hmac 7 | import base64 8 | import hashlib 9 | import requests 10 | 11 | # Obtain a CloudSploit API key and secret from the dashboard 12 | api_key = "replace-with-key" 13 | secret = "replace-with-secret" 14 | 15 | # Replace with the ID of the scan 16 | scan_id = "123" 17 | 18 | endpoint = "https://api.cloudsploit.com" 19 | path = "/v2/scans/" + scan_id 20 | method = "GET" 21 | timestamp = str(int(time.time() * 1000)) 22 | 23 | string = timestamp + method + path 24 | signature = hmac.new(secret, msg=string, digestmod=hashlib.sha256).hexdigest() 25 | 26 | hdr = { 27 | "Accept": "application/json", 28 | "X-API-Key": api_key, 29 | "X-Signature": signature, 30 | "X-Timestamp": timestamp, 31 | "content-type": "application/json" 32 | } 33 | 34 | r=requests.get(endpoint + path, headers=hdr); 35 | 36 | print r.text 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SaaS API Samples for Aqua 2 | 3 | 4 | 5 | # Table of Contents 6 | 7 | - [Python samples](#python-samples) 8 | - [JavaScript samples](#javascript-samples) 9 | - [Ruby samples](#ruby-samples) 10 | 11 | ## Python samples 12 | 13 | ``` 14 | cd sample-python 15 | ``` 16 | 17 | 18 | ## Javascript samples 19 | 20 | ``` 21 | cd sample-js 22 | ``` 23 | 24 | ## Ruby samples 25 | 26 | ``` 27 | cd sample-ruby 28 | ``` 29 | 30 | ### Install gems 31 | 32 | 33 | ``` 34 | bundle 35 | ``` 36 | 37 | ### Add API key and secret 38 | 39 | Create `.env` file for local environment variables. 40 | 41 | ``` 42 | cp sample.env .env 43 | ``` 44 | 45 | Edit `.env`. 46 | 47 | ``` 48 | vi .env 49 | ``` 50 | 51 | Update `API_KEY` and `API_SECRET` values. 52 | 53 | ``` 54 | API_KEY=api-key-123 55 | API_SECRET=api-secret-456 56 | ``` 57 | 58 | ### Run example 59 | 60 | The `tests.rb` example will query all current plugins from the API endpoint and return a JSON object. 61 | 62 | ``` 63 | ruby tests.rb 64 | ``` 65 | -------------------------------------------------------------------------------- /codelessPlugin/aquaplugin-APIGatewayPrivateEndpointsnew.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 988, 3 | "title": "API Gateway Private Endpoints new", 4 | "description": "Ensures that Amazon API Gateway APIs are only accessible through private endpoints.", 5 | "severity": "low", 6 | "more_info": "API Gateway APIs should be only accessible through private endpoints to ensure API security.", 7 | "recommended_action": "Set API Gateway API endpoint configuration to private", 8 | "link": "https://aws.amazon.com/blogs/compute/introducing-amazon-api-gateway-private-endpoints", 9 | "category": "API Gateway", 10 | "asl": { 11 | "apis": [ 12 | "APIGateway:getRestApis" 13 | ], 14 | "conditions": [ 15 | { 16 | "service": "apigateway", 17 | "api": "getRestApis", 18 | "property": "endpointConfiguration.types", 19 | "op": "CONTAINS", 20 | "value": "PRIVATE", 21 | "override": false, 22 | "transform": "NONE" 23 | } 24 | ], 25 | "version": 1 26 | } 27 | } -------------------------------------------------------------------------------- /codelessPlugin/aquaplugin-EKSKubernetesVersionNew.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 994, 3 | "title": "EKS Kubernetes Version New", 4 | "description": "Ensures the specific version of Kubernetes is installed on EKS clusters", 5 | "severity": "low", 6 | "more_info": "EKS supports provisioning clusters from several versions of Kubernetes. Clusters should be kept up to date to ensure Kubernetes security patches are applied.", 7 | "recommended_action": "Upgrade the version of Kubernetes on all EKS clusters to the latest available version.", 8 | "link": "https://docs.aws.amazon.com/eks/latest/userguide/kubernetes-versions.html", 9 | "category": "EKS", 10 | "asl": { 11 | "apis": [ 12 | "EKS:describeCluster", 13 | "EKS:listClusters" 14 | ], 15 | "conditions": [ 16 | { 17 | "service": "eks", 18 | "api": "describeCluster", 19 | "property": "cluster.version", 20 | "op": "GT", 21 | "value": "1.10", 22 | "override": false, 23 | "transform": "NONE" 24 | } 25 | ], 26 | "version": 1 27 | } 28 | } -------------------------------------------------------------------------------- /sample-js/make_request.js: -------------------------------------------------------------------------------- 1 | // This Node script will submit a GET to the 2 | // scans API endpoint to obtain a list of 3 | // available scans 4 | 5 | var crypto = require('crypto'); 6 | var moment = require('moment'); 7 | var request = require('request'); 8 | 9 | var key = ''; 10 | var secret = ''; 11 | var endpoint = 'https://4ija8kqhm7.execute-api.us-east-1.amazonaws.com/prod'; 12 | var path = '/v2/scans'; 13 | var timestamp = (moment.unix(new Date()))/1000; 14 | 15 | var string = timestamp + 'GET' + path; 16 | 17 | var hmac = crypto.createHmac('sha256', secret); 18 | hmac.setEncoding('hex'); 19 | hmac.write(string); 20 | hmac.end(); 21 | var signature = hmac.read(); 22 | 23 | var options = { 24 | method: 'GET', 25 | url: endpoint + path, 26 | headers: { 27 | 'X-API-Key': key, 28 | 'X-Signature': signature, 29 | 'X-Timestamp': timestamp, 30 | "Content-Type": "application/json" 31 | } 32 | }; 33 | 34 | request(options, function (error, response, body){ 35 | if (error) return console.log(error); 36 | console.log(JSON.stringify(response)); 37 | }); -------------------------------------------------------------------------------- /codelessPlugin/aquaplugin-S3BucketVersioningNew.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 986, 3 | "title": "S3 Bucket Versioning New", 4 | "description": "Ensures object versioning is enabled on S3 buckets", 5 | "severity": "low", 6 | "more_info": "Object versioning can help protect against the overwriting of objects or data loss in the event of a compromise.", 7 | "recommended_action": "Enable Bucketversioning", 8 | "link": "http://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html", 9 | "category": "S3", 10 | "asl": { 11 | "apis": [ 12 | "S3:getBucketVersioning", 13 | "S3:listBuckets" 14 | ], 15 | "version": 1, 16 | "conditions": [ 17 | { 18 | "service": "s3", 19 | "api": "getBucketVersioning", 20 | "property": "Status", 21 | "op": "EQ", 22 | "value": "Enabled", 23 | "override": false, 24 | "transform": "STRING" 25 | }, 26 | { 27 | "service": "s3", 28 | "api": "getBucketVersioning", 29 | "property": "Status", 30 | "op": "EXISTS", 31 | "value": "", 32 | "override": false, 33 | "transform": "NONE", 34 | "logical": "AND" 35 | } 36 | ] 37 | } 38 | } -------------------------------------------------------------------------------- /sample-python/legacy/scan_details_p3.py: -------------------------------------------------------------------------------- 1 | # This Python3 script will query for the details 2 | # of a scan given a scan ID (with appropriate access) 3 | 4 | import json 5 | import time 6 | import hmac 7 | import base64 8 | import hashlib 9 | import requests 10 | import os 11 | from os.path import join, dirname 12 | from dotenv import load_dotenv 13 | 14 | dotenv_path = join(dirname(__file__), '.env') 15 | load_dotenv(dotenv_path) 16 | 17 | # Obtain a CloudSploit API key and secret from the dashboard 18 | api_key = os.getenv('API_KEY') 19 | secret = os.getenv('API_SECRET') 20 | 21 | # Replace with the ID of the scan 22 | scan_id = "123" 23 | 24 | endpoint = "https://api.cloudsploit.com" 25 | path = "/v2/scansv2/" + scan_id 26 | method = "GET" 27 | timestamp = str(int(time.time() * 1000)) 28 | 29 | string = timestamp + method + path 30 | 31 | secret_bytes= bytes(secret , 'latin-1') 32 | string_bytes = bytes(string, 'latin-1') 33 | 34 | signature = hmac.new(secret_bytes, msg=string_bytes, digestmod=hashlib.sha256).hexdigest() 35 | 36 | hdr = { 37 | "Accept": "application/json", 38 | "X-API-Key": api_key, 39 | "X-Signature": signature, 40 | "X-Timestamp": timestamp, 41 | "content-type": "application/json" 42 | } 43 | 44 | r=requests.get(endpoint + path, headers=hdr); 45 | 46 | print(r.text) 47 | -------------------------------------------------------------------------------- /sample-python/main.py: -------------------------------------------------------------------------------- 1 | import json 2 | from helpers.aqua import list_keys 3 | from helpers.aqua import latest_scan 4 | from helpers.aqua import compliance_report 5 | 6 | """ 7 | Compliance program IDs 8 | 1 # PCI 9 | 2 # HIPAA 10 | 221 # SOC 2 type II 11 | 268 # ISO 27001 12 | ... 13 | """ 14 | program_id = 1 15 | 16 | 17 | """ 18 | Uncomment to get a list of all cloud accounts/projects/subscriptions (a/k/a "keys") 19 | """ 20 | # keys = list_keys() 21 | # print(json.dumps(keys, indent=2)) 22 | 23 | """ 24 | Replace with your key id 25 | """ 26 | key_id = 999 27 | 28 | 29 | """ 30 | get latest scan 31 | """ 32 | scan = latest_scan(key_id=key_id) 33 | 34 | 35 | """ 36 | get a compliance report 37 | """ 38 | report = compliance_report(scan_id=scan["id"], program_id=program_id) 39 | 40 | if len(report) > 0: 41 | statuses = [] 42 | 43 | for result in report: 44 | statuses.append(result["compliance"]) 45 | s = str(result["compliance"]).rjust(3, " ") 46 | print(f"Compliance: {s}% / {result['name']}") 47 | # uncomment to print full report details 48 | # print(json.dumps(report, indent=2)) 49 | 50 | avg = "{:.2f}".format(sum(statuses) / len(report)) 51 | print(f"Average for program {program_id}: {avg}%") 52 | else: 53 | print(f"No results for compliance program {program_id}.") 54 | -------------------------------------------------------------------------------- /sample-python/legacy/post_yaml_cloudformation.py: -------------------------------------------------------------------------------- 1 | # This Python script will submit a POST to the 2 | # CloudFormation scan API endpoint to obtain 3 | # potential security risks for a given template. 4 | 5 | import json 6 | import time 7 | import hmac 8 | import base64 9 | import hashlib 10 | import requests 11 | 12 | # Obtain a CloudSploit API key and secret from the dashboard 13 | api_key = "replace-with-key" 14 | secret = "replace-with-secret" 15 | 16 | # Load a YAML file from disk 17 | yaml_file_path = "/path/to/template.yaml" 18 | 19 | endpoint = "https://4ija8kqhm7.execute-api.us-east-1.amazonaws.com/prod" 20 | path = "/v2/cloudformations" 21 | method = "POST" 22 | timestamp = str(int(time.time() * 1000)) 23 | 24 | # Load the YAML file 25 | with open(yaml_file_path, 'r') as yaml_file: 26 | yaml_base64=base64.b64encode(yaml_file.read()) 27 | 28 | body = { 29 | "base64": yaml_base64 30 | } 31 | 32 | body_str = json.dumps(body, separators=(',', ':')) 33 | 34 | string = timestamp + method + path + body_str 35 | signature = hmac.new(secret, msg=string, digestmod=hashlib.sha256).hexdigest() 36 | 37 | hdr = { 38 | "Accept": "application/json", 39 | "X-API-Key": api_key, 40 | "X-Signature": signature, 41 | "X-Timestamp": timestamp, 42 | "content-type": "application/json" 43 | } 44 | 45 | r=requests.post(endpoint + path, headers=hdr, data=body_str); 46 | 47 | print r.text 48 | -------------------------------------------------------------------------------- /codelessPlugin/aquaplugin-SNSTopicCMKEncryptionnew.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 992, 3 | "title": "SNS Topic CMK Encryption new", 4 | "description": "Ensures Amazon SNS topics are encrypted with KMS Customer Master Keys (CMKs).", 5 | "severity": "low", 6 | "more_info": "AWS SNS topics should be encrypted with KMS Customer Master Keys (CMKs) instead of AWS managed-keys in order to have a more granular control over the SNS data-at-rest encryption and decryption process.", 7 | "recommended_action": "Update SNS topics to use Customer Master Keys (CMKs) for Server-Side Encryption.", 8 | "link": "https://docs.aws.amazon.com/sns/latest/dg/sns-server-side-encryption.html", 9 | "category": "SNS", 10 | "asl": { 11 | "apis": [ 12 | "SNS:getTopicAttributes", 13 | "SNS:listTopics" 14 | ], 15 | "conditions": [ 16 | { 17 | "service": "sns", 18 | "api": "getTopicAttributes", 19 | "property": "Attributes.KmsMasterKeyId", 20 | "op": "EXISTS", 21 | "value": "", 22 | "override": false, 23 | "transform": "NONE" 24 | }, 25 | { 26 | "service": "sns", 27 | "api": "getTopicAttributes", 28 | "property": "Attributes.KmsMasterKeyId", 29 | "op": "EQ", 30 | "value": "alias/aws/sns", 31 | "override": false, 32 | "transform": "STRING", 33 | "logical": "AND" 34 | } 35 | ], 36 | "version": 1 37 | } 38 | } -------------------------------------------------------------------------------- /sample-python/legacy/scan_list_p3_params.py: -------------------------------------------------------------------------------- 1 | # This Python3 script will query for a list of scans 2 | # including limit and offset values of a scan 3 | 4 | import json 5 | import time 6 | import hmac 7 | import base64 8 | import hashlib 9 | import requests 10 | import os 11 | from os.path import join, dirname 12 | from dotenv import load_dotenv 13 | 14 | dotenv_path = join(dirname(__file__), '.env') 15 | load_dotenv(dotenv_path) 16 | 17 | # Obtain a CloudSploit API key and secret from the dashboard 18 | api_key = os.getenv('API_KEY') 19 | secret = os.getenv('API_SECRET') 20 | 21 | # Include limit and offset here 22 | # Set the limit=1 and remove "offset" entirely (don't set to 0) 23 | # to retrieve the latest scan 24 | limit="1" 25 | offset="5" 26 | 27 | endpoint = "https://api.cloudsploit.com" 28 | path = "/v2/scans" 29 | args = "?limit=" + limit + "&offset=" + offset 30 | method = "GET" 31 | timestamp = str(int(time.time() * 1000)) 32 | 33 | string = timestamp + method + path 34 | 35 | secret_bytes= bytes(secret , 'latin-1') 36 | string_bytes = bytes(string, 'latin-1') 37 | 38 | signature = hmac.new(secret_bytes, msg=string_bytes, digestmod=hashlib.sha256).hexdigest() 39 | 40 | hdr = { 41 | "Accept": "application/json", 42 | "X-API-Key": api_key, 43 | "X-Signature": signature, 44 | "X-Timestamp": timestamp, 45 | "content-type": "application/json" 46 | } 47 | 48 | r=requests.get(endpoint + path + args, headers=hdr); 49 | 50 | print(r.text) 51 | -------------------------------------------------------------------------------- /codelessPlugin/aquaplugin-ELBv2HTTPSOnlyNew.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 995, 3 | "title": "ELBv2 HTTPS Only New", 4 | "description": "Ensures ELBs are configured to only accept connections on HTTPS ports.", 5 | "severity": "low", 6 | "more_info": "For maximum security, ELBs can be configured to only accept HTTPS connections. Standard HTTP connections directly and not rely on a redirect from HTTP. client application is configured to query HTTPS will be blocked. This should only be done if the ", 7 | "recommended_action": "Remove non-HTTPS listeners from load balancer.", 8 | "link": "http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/elb-security-policy-options.html", 9 | "category": "ELBv2", 10 | "asl": { 11 | "apis": [ 12 | "ELBv2:describeListeners", 13 | "ELBv2:describeLoadBalancers" 14 | ], 15 | "version": 1, 16 | "conditions": [ 17 | { 18 | "service": "elbv2", 19 | "api": "describeListeners", 20 | "property": "Listeners[*].Protocol", 21 | "op": "EQ", 22 | "value": "HTTP", 23 | "override": false, 24 | "transform": "STRING" 25 | }, 26 | { 27 | "service": "elbv2", 28 | "api": "describeListeners", 29 | "property": "Listeners[*].Protocol", 30 | "op": "EQ", 31 | "value": "SSL", 32 | "override": false, 33 | "transform": "STRING", 34 | "logical": "OR" 35 | } 36 | ] 37 | } 38 | } -------------------------------------------------------------------------------- /sample-python/readme.md: -------------------------------------------------------------------------------- 1 | ### Aqua SaaS Example - Compliance Status 2 | 3 | Simple Python example to collect the latest compliance status for an account for a given compliance program (e.g. GDPR, HIPAA, PCI, etc.) 4 | 5 | 6 | #### Prerequisites 7 | 8 | - Aqua SaaS API key & secret ([API keys](https://cloud.aquasec.com/cspm/#/apikeys)) 9 | - Python 3.9+ 10 | - Poetry ([install](https://python-poetry.org/docs/#installation)) 11 | 12 | 13 | #### Setup 14 | 15 | Install packages. 16 | 17 | ``` 18 | $ poetry install 19 | ``` 20 | 21 | Export environment variables with your Aqua API key and secret. 22 | 23 | ``` 24 | $ export AQUA_API_KEY=abc123d--example--wxyz 25 | $ export AQUA_API_SECRET=STgj--example--YAvtAB5zUOyDEFrwXRkK 26 | ``` 27 | 28 | Change the `key_id` in `main.py` to your key (cloud account/project/subscription). 29 | 30 | ```python 31 | """ 32 | Replace with your key id 33 | """ 34 | key_id = 999 35 | ``` 36 | 37 | #### Run 38 | 39 | ``` 40 | $ poetry run python main.py 41 | 42 | Compliance: 81% / Requirement 1 - Firewalls 43 | Compliance: 80% / Requirement 2 - Defaults 44 | Compliance: 86% / Requirement 3 - Cardholder Data 45 | Compliance: 100% / Requirement 4 - Encrypted Transmission 46 | Compliance: 6% / Requirement 6 - Secure Systems 47 | Compliance: 98% / Requirement 7 - Restrict Access 48 | Compliance: 69% / Requirement 8 - Identify Access 49 | Compliance: 32% / Requirement 10 - Track Access 50 | Compliance: 100% / Requirement 11 - Test Systems 51 | Average for program 1: 72.44% 52 | 53 | ``` -------------------------------------------------------------------------------- /codelessPlugin/aquaplugin-LambdaTracingEnabledNew.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 996, 3 | "title": "Lambda Tracing Enabled New", 4 | "description": "Ensures AWS Lambda functions have active tracing for X-Ray.", 5 | "severity": "low", 6 | "more_info": "AWS Lambda functions should have active tracing in order to gain visibility into the functions execution and performance.", 7 | "recommended_action": "Modify Lambda functions to activate tracing", 8 | "link": "https://docs.aws.amazon.com/lambda/latest/dg/services-xray.html", 9 | "category": "Lambda", 10 | "asl": { 11 | "apis": [ 12 | "Lambda:listFunctions" 13 | ], 14 | "version": 1, 15 | "conditions": [ 16 | { 17 | "service": "lambda", 18 | "api": "listFunctions", 19 | "property": "TracingConfig.Mode", 20 | "op": "EQ", 21 | "value": "ACTIVE", 22 | "override": false, 23 | "transform": "STRING" 24 | }, 25 | { 26 | "service": "lambda", 27 | "api": "listFunctions", 28 | "property": "TracingConfig.Mode", 29 | "op": "EQ", 30 | "value": "Active", 31 | "override": false, 32 | "transform": "STRING", 33 | "logical": "OR" 34 | }, 35 | { 36 | "service": "lambda", 37 | "api": "listFunctions", 38 | "property": "TracingConfig.Mode", 39 | "op": "EQ", 40 | "value": "active", 41 | "override": false, 42 | "transform": "STRING", 43 | "logical": "OR" 44 | } 45 | ] 46 | } 47 | } -------------------------------------------------------------------------------- /codelessPlugin/aquaplugin-KMSScheduledDeletionnew.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 990, 3 | "title": "KMS Scheduled Deletion new", 4 | "description": "Detects KMS keys that are scheduled for deletion", 5 | "severity": "low", 6 | "more_info": "Deleting a KMS key will permanently prevent all data encrypted using that key from being decrypted. Avoid deleting keys unless no encrypted data is in use.", 7 | "recommended_action": "Disable the key deletion before the scheduled deletion time.", 8 | "link": "http://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys.html", 9 | "category": "KMS", 10 | "asl": { 11 | "apis": [ 12 | "KMS:describeKey", 13 | "KMS:listKeys" 14 | ], 15 | "conditions": [ 16 | { 17 | "service": "kms", 18 | "api": "describeKey", 19 | "property": "KeyMetadata", 20 | "op": "EXISTS", 21 | "value": "", 22 | "override": false, 23 | "transform": "NONE" 24 | }, 25 | { 26 | "service": "kms", 27 | "api": "describeKey", 28 | "property": "KeyMetadata.KeyState", 29 | "op": "EXISTS", 30 | "value": "", 31 | "override": false, 32 | "transform": "NONE", 33 | "logical": "AND" 34 | }, 35 | { 36 | "service": "kms", 37 | "api": "describeKey", 38 | "property": "KeyMetadata.KeyState", 39 | "op": "EQ", 40 | "value": "PendingDeletion", 41 | "override": false, 42 | "transform": "STRING", 43 | "logical": "AND" 44 | } 45 | ], 46 | "version": 1 47 | } 48 | } -------------------------------------------------------------------------------- /sample-python/legacy/post_yaml_cloudformation_python3.py: -------------------------------------------------------------------------------- 1 | # This Python script will submit a POST to the 2 | # CloudFormation scan API endpoint to obtain 3 | # potential security risks for a given template. 4 | 5 | import json 6 | import time 7 | import hmac 8 | import base64 9 | import hashlib 10 | import requests 11 | import os 12 | from os.path import join, dirname 13 | from dotenv import load_dotenv 14 | 15 | dotenv_path = join(dirname(__file__), '.env') 16 | load_dotenv(dotenv_path) 17 | 18 | # Obtain a CloudSploit API key and secret from the dashboard 19 | api_key = os.getenv('API_KEY') 20 | secret = os.getenv('API_SECRET') 21 | cf_file = os.getenv('CLOUDFORMATION_YAML') 22 | 23 | # Get the sample.zip 24 | os.chdir('../') 25 | path = os.getcwd() 26 | 27 | # Load a YAML file from disk 28 | yaml_file_path = path + cf_file 29 | 30 | endpoint = "https://api.cloudsploit.com" 31 | path = "/v2/cloudformations" 32 | method = "POST" 33 | timestamp = str(int(time.time() * 1000)) 34 | 35 | # Load the YAML file 36 | with open(yaml_file_path, 'rb') as yaml_file: 37 | yaml_base64=base64.b64encode(yaml_file.read()) 38 | 39 | body = { 40 | "base64": yaml_base64.decode() 41 | } 42 | 43 | body_str = json.dumps(body, separators=(',', ':')) 44 | 45 | string = timestamp + method + path + body_str 46 | secret_bytes = bytes(secret, 'utf-8') 47 | string_bytes = bytes(string, 'utf-8') 48 | signature = hmac.new(secret_bytes, msg=string_bytes, digestmod=hashlib.sha256).hexdigest() 49 | 50 | hdr = { 51 | "Accept": "application/json", 52 | "X-API-Key": api_key, 53 | "X-Signature": signature, 54 | "X-Timestamp": timestamp, 55 | "content-type": "application/json" 56 | } 57 | 58 | r=requests.post(endpoint + path, headers=hdr, data=body_str); 59 | 60 | print(r.text) 61 | -------------------------------------------------------------------------------- /sample-python/legacy/post_terraform_folder.py: -------------------------------------------------------------------------------- 1 | # This Python script will submit a POST to the 2 | # Terraform scan API endpoint to obtain 3 | # potential security risks for a given ZIP file 4 | # of Terraform files/modules. 5 | 6 | import json 7 | import time 8 | import hmac 9 | import base64 10 | import hashlib 11 | import requests 12 | import os 13 | from os.path import join, dirname 14 | from dotenv import load_dotenv 15 | 16 | dotenv_path = join(dirname(__file__), '.env') 17 | load_dotenv(dotenv_path) 18 | 19 | # Obtain a CloudSploit API key and secret from the dashboard 20 | api_key = os.getenv('API_KEY') 21 | secret = os.getenv('API_SECRET') 22 | folder = os.getenv('FOLDER_TERRAFORM') 23 | 24 | # Get the sample.zip 25 | os.chdir('../') 26 | path = os.getcwd() 27 | 28 | # Load a ZIP file containing Terraform files 29 | zip_folder_path = path + folder 30 | 31 | endpoint = "https://api.cloudsploit.com" 32 | path = "/v2/terraforms" 33 | method = "POST" 34 | timestamp = str(int(time.time() * 1000)) 35 | 36 | # Load the YAML file 37 | with open(zip_folder_path, 'rb') as zip_file: 38 | zip_base64=base64.b64encode(zip_file.read()) 39 | 40 | body = { 41 | "base64": zip_base64.decode(), 42 | "folder": True 43 | } 44 | 45 | body_str = json.dumps(body, separators=(',', ':')) 46 | 47 | string = timestamp + method + path + body_str 48 | secret_bytes = bytes(secret, 'utf-8') 49 | string_bytes = bytes(string, 'utf-8') 50 | signature = hmac.new(secret_bytes, msg=string_bytes, digestmod=hashlib.sha256).hexdigest() 51 | 52 | hdr = { 53 | "Accept": "application/json", 54 | "X-API-Key": api_key, 55 | "X-Signature": signature, 56 | "X-Timestamp": timestamp, 57 | "content-type": "application/json" 58 | } 59 | 60 | r=requests.post(endpoint + path, headers=hdr, data=body_str); 61 | 62 | print(r.text) 63 | -------------------------------------------------------------------------------- /sample-python/helpers/aqua.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | from .auth import headers 4 | 5 | BASE_API_URL = "https://api.cloudsploit.com/v2" 6 | 7 | 8 | def list_keys() -> list | dict: 9 | """ 10 | List all cloud accounts (keys) 11 | """ 12 | 13 | url = f"{BASE_API_URL}/keys" 14 | keys = [] 15 | 16 | response = requests.get(url, headers=headers(url)) 17 | 18 | if response.status_code == 200: 19 | data = json.loads(response.text) 20 | 21 | for key in data["data"]: 22 | keys.append( 23 | { 24 | "id": key["id"], 25 | "name": key["name"], 26 | "cloud": key["cloud"], 27 | } 28 | ) 29 | 30 | return keys 31 | else: 32 | return {"status": response.status_code} 33 | 34 | 35 | def latest_scan(key_id: int = None) -> dict: 36 | """ 37 | Get latest scan id 38 | 39 | Reference: https://cloudsploit.docs.apiary.io/#reference/scans/scans-collection/get-list-all-scans 40 | """ 41 | 42 | if key_id == None: 43 | url = f"{BASE_API_URL}/scans?limit=1" 44 | else: 45 | url = f"{BASE_API_URL}/scans?limit=1&key_id={key_id}" 46 | 47 | response = requests.get(url, headers=headers(url)) 48 | 49 | if response.status_code == 200: 50 | data = json.loads(response.text) 51 | return data["data"][0] 52 | else: 53 | return {"status": response.status_code} 54 | 55 | 56 | def compliance_report(scan_id: int, program_id: int = 1): 57 | """ 58 | Get compliance report for a compliance program 59 | 60 | Reference: https://cloudsploit.docs.apiary.io/#reference/compliances/compliances-collection/get-list-all-compliances 61 | """ 62 | url = f"{BASE_API_URL}/compliances?scan_id={scan_id}&program_id={program_id}&summary=compliance" 63 | 64 | response = requests.get(url, headers=headers(url)) 65 | 66 | if response.status_code == 200: 67 | data = json.loads(response.text) 68 | return data["data"] 69 | else: 70 | return {"status": response.status_code} 71 | -------------------------------------------------------------------------------- /sample-js/realtime_scan.js: -------------------------------------------------------------------------------- 1 | var crypto = require('crypto'); 2 | var moment = require('moment'); 3 | var request = require('request'); 4 | var fs = require('fs'); 5 | 6 | // CHANGE THESE 7 | var key = ''; 8 | var secret = ''; 9 | var keyId = 1234; // The key_id you want to scan (obtain this via GET /keys) 10 | 11 | // Do not change this 12 | var baseUrl = 'https://api.cloudsploit.com'; 13 | var interval = 5000; 14 | 15 | var makeCall = function(method, path, query, body, callback) { 16 | var timestamp = (moment.unix(new Date()))/1000; 17 | var endpoint = baseUrl + path; 18 | 19 | var string = timestamp + method + path + 20 | (body && Object.keys(body).length > 0 ? JSON.stringify(body) : ''); 21 | 22 | var hmac = crypto.createHmac('sha256', secret); 23 | hmac.setEncoding('hex'); 24 | hmac.write(string); 25 | hmac.end(); 26 | var signature = hmac.read(); 27 | 28 | var options = { 29 | method: method, 30 | url: endpoint, 31 | headers: { 32 | 'Accept': 'application/json', 33 | 'X-API-Key': key, 34 | 'X-Signature': signature, 35 | 'X-Timestamp': timestamp, 36 | "content-type": "application/json" 37 | } 38 | }; 39 | 40 | if (body) options.json = body; 41 | 42 | console.log(JSON.stringify(options,null,2)); 43 | 44 | request(options, function (error, response, body){ 45 | if (error) return callback(error); 46 | // Uncomment this to see detailed response information 47 | //console.log(JSON.stringify(response)); 48 | callback(null, body); 49 | }); 50 | }; 51 | 52 | var body = { 53 | key_id: keyId 54 | }; 55 | 56 | makeCall('POST', '/v2/realtimes', '', body, function(err, data){ 57 | if (err) return console.log(err); 58 | console.log(data); 59 | 60 | setInterval(function(){ 61 | makeCall('GET', '/v2/realtimes/' + data.data.realtime_id.toString(), '', null, function(gErr, gData){ 62 | if (gErr) return console.log(gErr); 63 | // You may want to write this response to a file, or process it somehow else 64 | // The format will be: 65 | // {"status":200,"code":0,"data":{"status":"COMPLETE","message":null,"results":[_results_here_]}} 66 | console.log(gData); 67 | }); 68 | }, interval); 69 | }); -------------------------------------------------------------------------------- /codelessPlugin/aquaplugin-DynamoDBContinuousBackupsNew.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 993, 3 | "title": "DynamoDB Continuous Backups New", 4 | "description": "Ensures that Amazon DynamoDB tables have continuous backups enabled.", 5 | "severity": "low", 6 | "more_info": "DynamoDB tables should have Continuous Backups and Point-In-Time Recovery (PITR) features enabled to protect DynamoDB data against accidental data writes.", 7 | "recommended_action": "Enable Continuous Backups and Point-In-Time Recovery (PITR) features.", 8 | "link": "https://aws.amazon.com/blogs/aws/new-amazon-dynamodb-continuous-backups-and-point-in-time-recovery-pitr/", 9 | "category": "DynamoDB", 10 | "asl": { 11 | "apis": [ 12 | "DynamoDB:describeContinuousBackups", 13 | "DynamoDB:listTables" 14 | ], 15 | "conditions": [ 16 | { 17 | "service": "dynamodb", 18 | "api": "describeContinuousBackups", 19 | "property": "ContinuousBackupsDescription.ContinuousBackupsStatus", 20 | "op": "EXISTS", 21 | "value": "", 22 | "override": false, 23 | "transform": "NONE" 24 | }, 25 | { 26 | "service": "dynamodb", 27 | "api": "describeContinuousBackups", 28 | "property": "ContinuousBackupsDescription.ContinuousBackupsStatus", 29 | "op": "EQ", 30 | "value": "ENABLED", 31 | "override": false, 32 | "transform": "STRING", 33 | "logical": "AND" 34 | }, 35 | { 36 | "service": "dynamodb", 37 | "api": "describeContinuousBackups", 38 | "property": "ContinuousBackupsDescription.PointInTimeRecoveryDescription.PointInTimeRecoveryStatus", 39 | "op": "EXISTS", 40 | "value": "", 41 | "override": false, 42 | "transform": "NONE", 43 | "logical": "AND" 44 | }, 45 | { 46 | "service": "dynamodb", 47 | "api": "describeContinuousBackups", 48 | "property": "ContinuousBackupsDescription.PointInTimeRecoveryDescription.PointInTimeRecoveryStatus", 49 | "op": "EQ", 50 | "value": "ENABLED", 51 | "override": false, 52 | "transform": "STRING", 53 | "logical": "AND" 54 | } 55 | ], 56 | "version": 1 57 | } 58 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .venv 109 | .env 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | .DS_Store 117 | 118 | -------------------------------------------------------------------------------- /sample-python/legacy/realtime_scan.py: -------------------------------------------------------------------------------- 1 | # This Python script will submit a POST to the 2 | # realtime scan API endpoint to initiate a new 3 | # scan. It then polls the endpoint for results. 4 | 5 | import sys 6 | import json 7 | import time 8 | import hmac 9 | import base64 10 | import hashlib 11 | import requests 12 | 13 | # Obtain a CloudSploit API key and secret from the dashboard 14 | api_key = "replace-with-key" 15 | secret = "replace-with-secret" 16 | key_id = 1234 # The key_id you want to scan (obtain this via GET /keys) 17 | 18 | base_url = "https://api.cloudsploit.com" 19 | interval = 5 # Decreasing this value will result in being rate limited 20 | 21 | def make_call(method, path, body): 22 | timestamp = str(int(time.time() * 1000)) 23 | endpoint = base_url + path; 24 | 25 | if body: 26 | body_str = json.dumps(body, separators=(',', ':')) 27 | else: 28 | body_str = "" 29 | 30 | string = timestamp + method + path + body_str 31 | signature = hmac.new(secret, msg=string, digestmod=hashlib.sha256).hexdigest() 32 | 33 | hdr = { 34 | "Accept": "application/json", 35 | "X-API-Key": api_key, 36 | "X-Signature": signature, 37 | "X-Timestamp": timestamp, 38 | "content-type": "application/json" 39 | } 40 | 41 | # print method + " " + endpoint 42 | 43 | if method is "POST": 44 | r=requests.post(endpoint, headers=hdr, data=body_str); 45 | else: 46 | r=requests.get(endpoint, headers=hdr); 47 | 48 | return r.text 49 | 50 | body = { 51 | "key_id": key_id 52 | } 53 | 54 | realtime_scan = make_call("POST", "/v2/realtimes", body) 55 | 56 | if realtime_scan is None: 57 | print "Error initiating realtime scan: " + realtime_scan 58 | sys.exit() 59 | 60 | realtime_json = json.loads(realtime_scan) 61 | 62 | if "errors" in realtime_json: 63 | print(json.dumps(realtime_json, indent=4, separators=(',', ': '))) 64 | sys.exit() 65 | 66 | realtime_id = realtime_json["data"]["realtime_id"] 67 | 68 | if realtime_id is None: 69 | print "Error obtaining realtime ID" 70 | sys.exit() 71 | 72 | print "Scan created with realtime ID: " + str(realtime_id) 73 | 74 | while True: 75 | print "Polling for results..." 76 | realtime_result = make_call("GET", "/v2/realtimes/" + str(realtime_id), None) 77 | realtime_result_json = json.loads(realtime_result) 78 | realtime_status = realtime_result_json["data"]["status"] 79 | 80 | if realtime_status != "COMPLETE": 81 | print realtime_result 82 | time.sleep(interval) 83 | else: 84 | print(json.dumps(realtime_result_json, indent=4, separators=(',', ': '))) 85 | sys.exit() 86 | -------------------------------------------------------------------------------- /sample_cf.yaml: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion" : "2010-09-09", 3 | 4 | "Description" : "AWS CloudFormation Sample Template EC2InstanceSample: Create an Amazon EC2 instance running the Amazon Linux AMI. The AMI is chosen based on the region in which the stack is run. This example uses the default security group, so to SSH to the new instance using the KeyPair you enter, you will need to have port 22 open in your default security group. **WARNING** This template an Amazon EC2 instances. You will be billed for the AWS resources used if you create a stack from this template.", 5 | 6 | "Parameters" : { 7 | "KeyName" : { 8 | "Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instance", 9 | "Type" : "String" 10 | } 11 | }, 12 | 13 | "Mappings" : { 14 | "RegionMap" : { 15 | "us-east-1" : { "AMI" : "ami-7f418316" }, 16 | "us-west-1" : { "AMI" : "ami-951945d0" }, 17 | "us-west-2" : { "AMI" : "ami-16fd7026" }, 18 | "eu-west-1" : { "AMI" : "ami-24506250" }, 19 | "sa-east-1" : { "AMI" : "ami-3e3be423" }, 20 | "ap-southeast-1" : { "AMI" : "ami-74dda626" }, 21 | "ap-southeast-2" : { "AMI" : "ami-b3990e89" }, 22 | "ap-northeast-1" : { "AMI" : "ami-dcfa4edd" } 23 | } 24 | }, 25 | 26 | "Resources" : { 27 | "Ec2Instance" : { 28 | "Type" : "AWS::EC2::Instance", 29 | "Properties" : { 30 | "KeyName" : { "Ref" : "KeyName" }, 31 | "ImageId" : { "Fn::FindInMap" : [ "RegionMap", { "Ref" : "AWS::Region" }, "AMI" ]}, 32 | "UserData" : { "Fn::Base64" : "80" } 33 | } 34 | } 35 | }, 36 | 37 | "Outputs" : { 38 | "InstanceId" : { 39 | "Description" : "InstanceId of the newly created EC2 instance", 40 | "Value" : { "Ref" : "Ec2Instance" } 41 | }, 42 | "AZ" : { 43 | "Description" : "Availability Zone of the newly created EC2 instance", 44 | "Value" : { "Fn::GetAtt" : [ "Ec2Instance", "AvailabilityZone" ] } 45 | }, 46 | "PublicIP" : { 47 | "Description" : "Public IP address of the newly created EC2 instance", 48 | "Value" : { "Fn::GetAtt" : [ "Ec2Instance", "PublicIp" ] } 49 | }, 50 | "PrivateIP" : { 51 | "Description" : "Private IP address of the newly created EC2 instance", 52 | "Value" : { "Fn::GetAtt" : [ "Ec2Instance", "PrivateIp" ] } 53 | }, 54 | "PublicDNS" : { 55 | "Description" : "Public DNSName of the newly created EC2 instance", 56 | "Value" : { "Fn::GetAtt" : [ "Ec2Instance", "PublicDnsName" ] } 57 | }, 58 | "PrivateDNS" : { 59 | "Description" : "Private DNSName of the newly created EC2 instance", 60 | "Value" : { "Fn::GetAtt" : [ "Ec2Instance", "PrivateDnsName" ] } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /sample-python/legacy/realtime_scan_p3.py: -------------------------------------------------------------------------------- 1 | # This Python script will submit a POST to the 2 | # realtime scan API endpoint to initiate a new 3 | # scan. It then polls the endpoint for results. 4 | 5 | import sys 6 | import json 7 | import time 8 | import hmac 9 | import base64 10 | import hashlib 11 | import requests 12 | import os 13 | from os.path import join, dirname 14 | from dotenv import load_dotenv 15 | 16 | dotenv_path = join(dirname(__file__), '.env') 17 | load_dotenv(dotenv_path) 18 | 19 | # Obtain a CloudSploit API key and secret from the dashboard 20 | api_key = os.getenv('API_KEY') 21 | secret = os.getenv('API_SECRET') 22 | key_id = int(os.getenv('KEY_ID')) 23 | 24 | base_url = "https://api.cloudsploit.com" 25 | interval = 5 # Decreasing this value will result in being rate limited 26 | 27 | def make_call(method, path, body): 28 | timestamp = str(int(time.time() * 1000)) 29 | endpoint = base_url + path; 30 | 31 | if body: 32 | body_str = json.dumps(body, separators=(',', ':')) 33 | else: 34 | body_str = "" 35 | 36 | string = timestamp + method + path + body_str 37 | 38 | secret_bytes= bytes(secret , 'latin-1') 39 | string_bytes = bytes(string, 'latin-1') 40 | 41 | signature = hmac.new(secret_bytes, msg=string_bytes, digestmod=hashlib.sha256).hexdigest() 42 | 43 | hdr = { 44 | "Accept": "application/json", 45 | "X-API-Key": api_key, 46 | "X-Signature": signature, 47 | "X-Timestamp": timestamp, 48 | "content-type": "application/json" 49 | } 50 | 51 | # print method + " " + endpoint 52 | 53 | if method == "POST": 54 | r=requests.post(endpoint, headers=hdr, data=body_str); 55 | else: 56 | r=requests.get(endpoint, headers=hdr); 57 | 58 | return r.text 59 | 60 | body = { 61 | "key_id": key_id, 62 | "save": True 63 | } 64 | 65 | realtime_scan = make_call("POST", "/v2/realtimes", body) 66 | 67 | if realtime_scan is None: 68 | print("Error initiating realtime scan: " + realtime_scan) 69 | sys.exit() 70 | 71 | realtime_json = json.loads(realtime_scan) 72 | 73 | if "errors" in realtime_json: 74 | print(json.dumps(realtime_json, indent=4, separators=(',', ': '))) 75 | sys.exit() 76 | 77 | realtime_id = realtime_json["data"]["realtime_id"] 78 | 79 | if realtime_id is None: 80 | print("Error obtaining realtime ID") 81 | sys.exit() 82 | 83 | print("Scan created with realtime ID: " + str(realtime_id)) 84 | 85 | while True: 86 | print("Polling for results...") 87 | realtime_result = make_call("GET", "/v2/realtimes/" + str(realtime_id), None) 88 | realtime_result_json = json.loads(realtime_result) 89 | realtime_status = realtime_result_json["data"]["status"] 90 | 91 | if realtime_status != "COMPLETE": 92 | print(realtime_result) 93 | time.sleep(interval) 94 | else: 95 | print(json.dumps(realtime_result_json, indent=4, separators=(',', ': '))) 96 | sys.exit() 97 | -------------------------------------------------------------------------------- /sample-js/Inventory-AWS.js: -------------------------------------------------------------------------------- 1 | // This script will quantify resources available in the AWS accounts configured in Aqua CSPM 2 | 3 | var async = require('async'); 4 | var crypto = require('crypto'); 5 | var moment = require('moment'); 6 | var request = require('request'); 7 | 8 | var key = 'Aqua-API-Key'; 9 | var secret = 'Aqua-API-Secret'; 10 | var baseUrl = 'https://api.cloudsploit.com'; 11 | 12 | fullResourceObj = {}; 13 | function createRequest(path, method, queryStringParams) { 14 | var timestamp = (moment.unix(new Date()))/1000; 15 | var string = timestamp + method + path; 16 | 17 | var hmac = crypto.createHmac('sha256', secret); 18 | hmac.setEncoding('hex'); 19 | hmac.write(string); 20 | hmac.end(); 21 | var signature = hmac.read(); 22 | 23 | var options = { 24 | method: method, 25 | url: baseUrl + path + (queryStringParams ? queryStringParams : ''), 26 | headers: { 27 | 'Accept': 'application/json', 28 | 'X-API-Key': key, 29 | 'X-Signature': signature, 30 | 'X-Timestamp': timestamp, 31 | "content-type": "application/json" 32 | } 33 | } 34 | return options; 35 | } 36 | 37 | function collect() { 38 | var options = createRequest('/v2/keys', 'GET'); 39 | // this is getting all keys from account/group 40 | request(options, function (error, response, body){ 41 | if (error || !body) return callback(error || 'No response body'); 42 | var bodyObj = JSON.parse(body); 43 | if (bodyObj && bodyObj.data) { 44 | // looping through keys 45 | async.eachLimit(bodyObj.data, 15, function(key, kCb) { 46 | if (key.id) { 47 | // if adding a new cloud update here. 48 | let resourcesToCollect; 49 | if (key.cloud === 'aws') { 50 | var AWSResourcesToCollect = { 51 | ':s3:': 0, 52 | ':ec2:': 0, 53 | ':rds:': 0, 54 | ':cloudfront:': 0, 55 | ':eks:': 0, 56 | ':ecr:': 0, 57 | 'elasticloadbalancing:': 0, 58 | ':sns:': 0, 59 | ':sqs:': 0, 60 | ':dynamodb:': 0, 61 | } 62 | resourcesToCollect = AWSResourcesToCollect; 63 | } 64 | else 65 | { 66 | return kCb(); 67 | } 68 | fullResourceObj[key.name] = {}; 69 | // this is to get usage for a specific key 70 | let usageOptions = createRequest('/v2/usage', 'GET', `?key_id=${key.id}`); 71 | request(usageOptions, function(uErr, uResp, uBody) { 72 | if (uErr || !uBody) { 73 | console.log(uErr || `No Usage response from key: ${key.name}`); 74 | return kCb(); 75 | } 76 | var uBodyObj = JSON.parse(uBody); 77 | 78 | async.each(Object.keys(resourcesToCollect), function(resourceIdentifier, rCb) { 79 | uBodyObj.data.forEach(resource => { 80 | if (resource.includes(resourceIdentifier)) { 81 | resourcesToCollect[resourceIdentifier]++; 82 | } 83 | }) 84 | rCb(); 85 | }, function() { 86 | fullResourceObj[key.name] = resourcesToCollect; 87 | return kCb(); 88 | }) 89 | 90 | }) 91 | } 92 | }, function() { 93 | // this is the final part of the whole function 94 | console.log(fullResourceObj); 95 | }) 96 | } 97 | 98 | }); 99 | } 100 | collect(); 101 | -------------------------------------------------------------------------------- /sample-js/Inventory-Azure.js: -------------------------------------------------------------------------------- 1 | // This script will quantify resources available in the Azure subscriptions configured in Aqua CSPM 2 | 3 | var async = require('async'); 4 | var crypto = require('crypto'); 5 | var moment = require('moment'); 6 | var request = require('request'); 7 | 8 | var key = 'Aqua-API-Key'; 9 | var secret = 'Aqua-API-Secret'; 10 | var baseUrl = 'https://api.cloudsploit.com'; 11 | 12 | fullResourceObj = {}; 13 | function createRequest(path, method, queryStringParams) { 14 | var timestamp = (moment.unix(new Date()))/1000; 15 | var string = timestamp + method + path; 16 | 17 | var hmac = crypto.createHmac('sha256', secret); 18 | hmac.setEncoding('hex'); 19 | hmac.write(string); 20 | hmac.end(); 21 | var signature = hmac.read(); 22 | 23 | var options = { 24 | method: method, 25 | url: baseUrl + path + (queryStringParams ? queryStringParams : ''), 26 | headers: { 27 | 'Accept': 'application/json', 28 | 'X-API-Key': key, 29 | 'X-Signature': signature, 30 | 'X-Timestamp': timestamp, 31 | "content-type": "application/json" 32 | } 33 | } 34 | return options; 35 | } 36 | 37 | function collect() { 38 | var options = createRequest('/v2/keys', 'GET'); 39 | // this is getting all keys from account/group 40 | request(options, function (error, response, body){ 41 | if (error || !body) return callback(error || 'No response body'); 42 | var bodyObj = JSON.parse(body); 43 | if (bodyObj && bodyObj.data) { 44 | // looping through keys 45 | async.eachLimit(bodyObj.data, 10, function(key, kCb) { 46 | if (key.id) { 47 | let resourcesToCollect; 48 | if (key.cloud === 'azure') { 49 | var AzureResourceToCollect = { 50 | 'microsoft.storage/storageaccounts': 0, 51 | 'microsoft.network/virtualnetworks': 0, 52 | 'microsoft.sql/servers': 0, 53 | 'microsoft.web/sites': 0, 54 | 'microsoft.network/networksecuritygroups': 0, 55 | 'microsoft.authorization/policyassignments': 0, 56 | 'microsoft.authorization/policydefinitions': 0, 57 | 'microsoft.network/networkwatchers': 0, 58 | 'microsoft.security/securitycontacts': 0, 59 | 'microsoft.security/autoprovisioningsettings': 0, 60 | 'microsoft.compute/virtualmachines': 0, 61 | 'microsoft.keyvault/vaults': 0, 62 | 'microsoft.insights/activitylogalerts': 0, 63 | 'microsoft.compute/disks': 0, 64 | 'microsoft.containerservice/managedclusters': 0, 65 | 'microsoft.insights/logprofiles': 0, 66 | 'microsoft.cdn/profiles': 0, 67 | 'microsoft.authorization/roledefinitions': 0, 68 | 'microsoft.authorization/locks': 0, 69 | 'microsoft.network/loadbalancers': 0, 70 | 'microsoft.containerregistry/registries': 0, 71 | 'microsoft.security/pricings': 0, 72 | 'microsoft.compute/availabilitysets': 0, 73 | 'microsoft.compute/virtualmachinescalesets': 0, 74 | 'microsoft.insights/autoscalesettings': 0, 75 | 'microsoft.insights/diagnosticsettings': 0, 76 | 'microsoft.dbformysql/servers': 0, 77 | 'microsoft.dbforpostgresql/servers': 0, 78 | } 79 | resourcesToCollect = AzureResourceToCollect; 80 | } else { 81 | return kCb(); 82 | } 83 | fullResourceObj[key.name] = {}; 84 | // this is to get usage for a specific key 85 | let usageOptions = createRequest('/v2/usage', 'GET', `?key_id=${key.id}`); 86 | request(usageOptions, function(uErr, uResp, uBody) { 87 | if (uErr || !uBody) { 88 | console.log(uErr || `No Usage response from key: ${key.name}`); 89 | return kCb(); 90 | } 91 | var uBodyObj = JSON.parse(uBody); 92 | 93 | async.each(Object.keys(resourcesToCollect), function(resourceIdentifier, rCb) { 94 | uBodyObj.data.forEach(resource => { 95 | if (resource.includes(resourceIdentifier)) { 96 | resourcesToCollect[resourceIdentifier]++; 97 | } 98 | }) 99 | rCb(); 100 | }, function() { 101 | fullResourceObj[key.name] = resourcesToCollect; 102 | return kCb(); 103 | }) 104 | 105 | }) 106 | } 107 | }, function() { 108 | console.log(fullResourceObj); 109 | }) 110 | } 111 | 112 | }); 113 | } 114 | collect(); 115 | -------------------------------------------------------------------------------- /purposely_insecure_terraform.tf: -------------------------------------------------------------------------------- 1 | /* 2 | * This example terraform file is intentionally insecure. 3 | * It is intended for use in https://cloud.aquasec.com/terraform 4 | */ 5 | 6 | 7 | # Specify the provider and access details 8 | provider "aws" { 9 | region = "${var.aws_region}" 10 | } 11 | 12 | resource "aws_vpc" "default" { 13 | cidr_block = "10.0.0.0/16" 14 | enable_dns_hostnames = true 15 | 16 | tags = { 17 | Name = "tf_test" 18 | } 19 | } 20 | 21 | resource "aws_subnet" "tf_test_subnet" { 22 | vpc_id = "${aws_vpc.default.id}" 23 | cidr_block = "10.0.0.0/24" 24 | map_public_ip_on_launch = true 25 | 26 | tags = { 27 | Name = "tf_test_subnet" 28 | } 29 | } 30 | 31 | resource "aws_internet_gateway" "gw" { 32 | vpc_id = "${aws_vpc.default.id}" 33 | 34 | tags = { 35 | Name = "tf_test_ig" 36 | } 37 | } 38 | 39 | resource "aws_route_table" "r" { 40 | vpc_id = "${aws_vpc.default.id}" 41 | 42 | route { 43 | cidr_block = "0.0.0.0/0" 44 | gateway_id = "${aws_internet_gateway.gw.id}" 45 | } 46 | 47 | tags = { 48 | Name = "aws_route_table" 49 | } 50 | } 51 | 52 | resource "aws_route_table_association" "a" { 53 | subnet_id = "${aws_subnet.tf_test_subnet.id}" 54 | route_table_id = "${aws_route_table.r.id}" 55 | } 56 | 57 | # Our default security group to access 58 | # the instances over SSH and HTTP 59 | resource "aws_security_group" "default" { 60 | name = "instance_sg" 61 | description = "Used in the terraform" 62 | vpc_id = "${aws_vpc.default.id}" 63 | 64 | # SSH access from anywhere 65 | ingress { 66 | from_port = 22 67 | to_port = 22 68 | protocol = "tcp" 69 | cidr_blocks = ["0.0.0.0/0"] 70 | } 71 | 72 | # HTTP access from anywhere 73 | ingress { 74 | from_port = 80 75 | to_port = 80 76 | protocol = "tcp" 77 | cidr_blocks = ["0.0.0.0/0"] 78 | } 79 | 80 | # outbound internet access 81 | egress { 82 | from_port = 0 83 | to_port = 0 84 | protocol = "-1" 85 | cidr_blocks = ["0.0.0.0/0"] 86 | } 87 | } 88 | 89 | # Our elb security group to access 90 | # the ELB over HTTP 91 | resource "aws_security_group" "elb" { 92 | name = "elb_sg" 93 | description = "Used in the terraform" 94 | 95 | vpc_id = "${aws_vpc.default.id}" 96 | 97 | # HTTP access from anywhere 98 | ingress { 99 | from_port = 80 100 | to_port = 80 101 | protocol = "tcp" 102 | cidr_blocks = ["0.0.0.0/0"] 103 | } 104 | 105 | # outbound internet access 106 | egress { 107 | from_port = 0 108 | to_port = 0 109 | protocol = "-1" 110 | cidr_blocks = ["0.0.0.0/0"] 111 | } 112 | 113 | # ensure the VPC has an Internet gateway or this step will fail 114 | depends_on = ["aws_internet_gateway.gw"] 115 | } 116 | 117 | resource "aws_elb" "web" { 118 | name = "example-elb" 119 | 120 | # The same availability zone as our instance 121 | subnets = ["${aws_subnet.tf_test_subnet.id}"] 122 | 123 | security_groups = ["${aws_security_group.elb.id}"] 124 | 125 | listener { 126 | instance_port = 80 127 | instance_protocol = "http" 128 | lb_port = 80 129 | lb_protocol = "http" 130 | } 131 | 132 | health_check { 133 | healthy_threshold = 2 134 | unhealthy_threshold = 2 135 | timeout = 3 136 | target = "HTTP:80/" 137 | interval = 30 138 | } 139 | 140 | # The instance is registered automatically 141 | 142 | instances = ["${aws_instance.web.id}"] 143 | cross_zone_load_balancing = true 144 | idle_timeout = 400 145 | connection_draining = true 146 | connection_draining_timeout = 400 147 | } 148 | 149 | resource "aws_lb_cookie_stickiness_policy" "default" { 150 | name = "lbpolicy" 151 | load_balancer = "${aws_elb.web.id}" 152 | lb_port = 80 153 | cookie_expiration_period = 600 154 | } 155 | 156 | resource "aws_instance" "web" { 157 | instance_type = "t2.micro" 158 | 159 | # Lookup the correct AMI based on the region 160 | # we specified 161 | ami = "${lookup(var.aws_amis, var.aws_region)}" 162 | 163 | # The name of our SSH keypair you've created and downloaded 164 | # from the AWS console. 165 | # 166 | # https://console.aws.amazon.com/ec2/v2/home?region=us-west-2#KeyPairs: 167 | # 168 | key_name = "${var.key_name}" 169 | 170 | # Our Security group to allow HTTP and SSH access 171 | vpc_security_group_ids = ["${aws_security_group.default.id}"] 172 | subnet_id = "${aws_subnet.tf_test_subnet.id}" 173 | user_data = "${file("userdata.sh")}" 174 | 175 | #Instance tags 176 | 177 | tags = { 178 | Name = "elb-example" 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /sample-python/poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "black" 3 | version = "22.1.0" 4 | description = "The uncompromising code formatter." 5 | category = "dev" 6 | optional = false 7 | python-versions = ">=3.6.2" 8 | 9 | [package.dependencies] 10 | click = ">=8.0.0" 11 | mypy-extensions = ">=0.4.3" 12 | pathspec = ">=0.9.0" 13 | platformdirs = ">=2" 14 | tomli = ">=1.1.0" 15 | 16 | [package.extras] 17 | colorama = ["colorama (>=0.4.3)"] 18 | d = ["aiohttp (>=3.7.4)"] 19 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 20 | uvloop = ["uvloop (>=0.15.2)"] 21 | 22 | [[package]] 23 | name = "certifi" 24 | version = "2021.10.8" 25 | description = "Python package for providing Mozilla's CA Bundle." 26 | category = "main" 27 | optional = false 28 | python-versions = "*" 29 | 30 | [[package]] 31 | name = "charset-normalizer" 32 | version = "2.0.11" 33 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 34 | category = "main" 35 | optional = false 36 | python-versions = ">=3.5.0" 37 | 38 | [package.extras] 39 | unicode_backport = ["unicodedata2"] 40 | 41 | [[package]] 42 | name = "click" 43 | version = "8.0.3" 44 | description = "Composable command line interface toolkit" 45 | category = "dev" 46 | optional = false 47 | python-versions = ">=3.6" 48 | 49 | [package.dependencies] 50 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 51 | 52 | [[package]] 53 | name = "colorama" 54 | version = "0.4.4" 55 | description = "Cross-platform colored terminal text." 56 | category = "dev" 57 | optional = false 58 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 59 | 60 | [[package]] 61 | name = "idna" 62 | version = "3.3" 63 | description = "Internationalized Domain Names in Applications (IDNA)" 64 | category = "main" 65 | optional = false 66 | python-versions = ">=3.5" 67 | 68 | [[package]] 69 | name = "mypy-extensions" 70 | version = "0.4.3" 71 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 72 | category = "dev" 73 | optional = false 74 | python-versions = "*" 75 | 76 | [[package]] 77 | name = "pathspec" 78 | version = "0.9.0" 79 | description = "Utility library for gitignore style pattern matching of file paths." 80 | category = "dev" 81 | optional = false 82 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 83 | 84 | [[package]] 85 | name = "platformdirs" 86 | version = "2.5.0" 87 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 88 | category = "dev" 89 | optional = false 90 | python-versions = ">=3.7" 91 | 92 | [package.extras] 93 | docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] 94 | test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] 95 | 96 | [[package]] 97 | name = "python-dotenv" 98 | version = "0.19.2" 99 | description = "Read key-value pairs from a .env file and set them as environment variables" 100 | category = "main" 101 | optional = false 102 | python-versions = ">=3.5" 103 | 104 | [package.extras] 105 | cli = ["click (>=5.0)"] 106 | 107 | [[package]] 108 | name = "requests" 109 | version = "2.27.1" 110 | description = "Python HTTP for Humans." 111 | category = "main" 112 | optional = false 113 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 114 | 115 | [package.dependencies] 116 | certifi = ">=2017.4.17" 117 | charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} 118 | idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} 119 | urllib3 = ">=1.21.1,<1.27" 120 | 121 | [package.extras] 122 | socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] 123 | use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] 124 | 125 | [[package]] 126 | name = "tomli" 127 | version = "2.0.1" 128 | description = "A lil' TOML parser" 129 | category = "dev" 130 | optional = false 131 | python-versions = ">=3.7" 132 | 133 | [[package]] 134 | name = "urllib3" 135 | version = "1.26.8" 136 | description = "HTTP library with thread-safe connection pooling, file post, and more." 137 | category = "main" 138 | optional = false 139 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" 140 | 141 | [package.extras] 142 | brotli = ["brotlipy (>=0.6.0)"] 143 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] 144 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 145 | 146 | [metadata] 147 | lock-version = "1.1" 148 | python-versions = "^3.10" 149 | content-hash = "8ac0733c7cadb197324dd6e86ebab183e852ee9d0de3f60cdabe0468ae989ebe" 150 | 151 | [metadata.files] 152 | black = [ 153 | {file = "black-22.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6"}, 154 | {file = "black-22.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866"}, 155 | {file = "black-22.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e21e1f1efa65a50e3960edd068b6ae6d64ad6235bd8bfea116a03b21836af71"}, 156 | {file = "black-22.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f69158a7d120fd641d1fa9a921d898e20d52e44a74a6fbbcc570a62a6bc8ab"}, 157 | {file = "black-22.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:228b5ae2c8e3d6227e4bde5920d2fc66cc3400fde7bcc74f480cb07ef0b570d5"}, 158 | {file = "black-22.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b1a5ed73ab4c482208d20434f700d514f66ffe2840f63a6252ecc43a9bc77e8a"}, 159 | {file = "black-22.1.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35944b7100af4a985abfcaa860b06af15590deb1f392f06c8683b4381e8eeaf0"}, 160 | {file = "black-22.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7835fee5238fc0a0baf6c9268fb816b5f5cd9b8793423a75e8cd663c48d073ba"}, 161 | {file = "black-22.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dae63f2dbf82882fa3b2a3c49c32bffe144970a573cd68d247af6560fc493ae1"}, 162 | {file = "black-22.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fa1db02410b1924b6749c245ab38d30621564e658297484952f3d8a39fce7e8"}, 163 | {file = "black-22.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c8226f50b8c34a14608b848dc23a46e5d08397d009446353dad45e04af0c8e28"}, 164 | {file = "black-22.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2d6f331c02f0f40aa51a22e479c8209d37fcd520c77721c034517d44eecf5912"}, 165 | {file = "black-22.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:742ce9af3086e5bd07e58c8feb09dbb2b047b7f566eb5f5bc63fd455814979f3"}, 166 | {file = "black-22.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fdb8754b453fb15fad3f72cd9cad3e16776f0964d67cf30ebcbf10327a3777a3"}, 167 | {file = "black-22.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5660feab44c2e3cb24b2419b998846cbb01c23c7fe645fee45087efa3da2d61"}, 168 | {file = "black-22.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:6f2f01381f91c1efb1451998bd65a129b3ed6f64f79663a55fe0e9b74a5f81fd"}, 169 | {file = "black-22.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:efbadd9b52c060a8fc3b9658744091cb33c31f830b3f074422ed27bad2b18e8f"}, 170 | {file = "black-22.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8871fcb4b447206904932b54b567923e5be802b9b19b744fdff092bd2f3118d0"}, 171 | {file = "black-22.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccad888050f5393f0d6029deea2a33e5ae371fd182a697313bdbd835d3edaf9c"}, 172 | {file = "black-22.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07e5c049442d7ca1a2fc273c79d1aecbbf1bc858f62e8184abe1ad175c4f7cc2"}, 173 | {file = "black-22.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:373922fc66676133ddc3e754e4509196a8c392fec3f5ca4486673e685a421321"}, 174 | {file = "black-22.1.0-py3-none-any.whl", hash = "sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d"}, 175 | {file = "black-22.1.0.tar.gz", hash = "sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5"}, 176 | ] 177 | certifi = [ 178 | {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, 179 | {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, 180 | ] 181 | charset-normalizer = [ 182 | {file = "charset-normalizer-2.0.11.tar.gz", hash = "sha256:98398a9d69ee80548c762ba991a4728bfc3836768ed226b3945908d1a688371c"}, 183 | {file = "charset_normalizer-2.0.11-py3-none-any.whl", hash = "sha256:2842d8f5e82a1f6aa437380934d5e1cd4fcf2003b06fed6940769c164a480a45"}, 184 | ] 185 | click = [ 186 | {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, 187 | {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, 188 | ] 189 | colorama = [ 190 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 191 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 192 | ] 193 | idna = [ 194 | {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, 195 | {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, 196 | ] 197 | mypy-extensions = [ 198 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 199 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 200 | ] 201 | pathspec = [ 202 | {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, 203 | {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, 204 | ] 205 | platformdirs = [ 206 | {file = "platformdirs-2.5.0-py3-none-any.whl", hash = "sha256:30671902352e97b1eafd74ade8e4a694782bd3471685e78c32d0fdfd3aa7e7bb"}, 207 | {file = "platformdirs-2.5.0.tar.gz", hash = "sha256:8ec11dfba28ecc0715eb5fb0147a87b1bf325f349f3da9aab2cd6b50b96b692b"}, 208 | ] 209 | python-dotenv = [ 210 | {file = "python-dotenv-0.19.2.tar.gz", hash = "sha256:a5de49a31e953b45ff2d2fd434bbc2670e8db5273606c1e737cc6b93eff3655f"}, 211 | {file = "python_dotenv-0.19.2-py2.py3-none-any.whl", hash = "sha256:32b2bdc1873fd3a3c346da1c6db83d0053c3c62f28f1f38516070c4c8971b1d3"}, 212 | ] 213 | requests = [ 214 | {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, 215 | {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, 216 | ] 217 | tomli = [ 218 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 219 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 220 | ] 221 | urllib3 = [ 222 | {file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"}, 223 | {file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"}, 224 | ] 225 | -------------------------------------------------------------------------------- /sample-js/generate_new_key_aws.js: -------------------------------------------------------------------------------- 1 | var crypto = require('crypto'); 2 | var async = require('async'); 3 | var moment = require('moment'); 4 | var request = require('request'); 5 | const AWS = require('aws-sdk'); 6 | 7 | // aqua cspm rest api endpoint 8 | var endpoint = 'https://api.cloudsploit.com'; 9 | 10 | // the apikey and secret from aqua cspm 11 | var key = ''; 12 | var secret = ''; 13 | 14 | // iam role and policy name to be used 15 | let roleName = 'Aqua-CSPM-Scanner-Role'; 16 | let policyName = 'aqua-cspm-supplemental-policy'; 17 | 18 | // name of the group to add the cloud accounts into. if no group is added, the Default group will be assigned to all cloud accounts. 19 | let groupToAdd; 20 | 21 | // list of AWS accounts to onboard, the name of each will be used as the saved name for the cloud account. 22 | let cloudAccounts = { 23 | 'aws_dev' : { 24 | accessKeyId: '', 25 | secretAccessKey: '' 26 | }, 27 | 'aws_prod' : { 28 | accessKeyId: '', 29 | secretAccessKey: '' 30 | }, 31 | }; 32 | 33 | 34 | 35 | // helper functions 36 | var setRequestOptions = function(path, method, body) { 37 | let timestamp = (moment.unix(new Date()))/1000; 38 | 39 | let string = timestamp + method + path + body; 40 | 41 | let hmac = crypto.createHmac('sha256', secret); 42 | hmac.setEncoding('hex'); 43 | hmac.write(string); 44 | hmac.end(); 45 | 46 | let signature = hmac.read(); 47 | 48 | let returnOptions = { 49 | method: method, 50 | url: endpoint + path, 51 | headers: { 52 | 'X-API-Key': key, 53 | 'X-Signature': signature, 54 | 'X-Timestamp': timestamp, 55 | "Content-Type": "application/json" 56 | } 57 | }; 58 | 59 | if (body.length) { 60 | returnOptions.body = body; 61 | } 62 | 63 | return returnOptions; 64 | }; 65 | var addNewKey = function(createKeyBody, kcb) { 66 | let path = '/v2/keys'; 67 | 68 | let options = setRequestOptions(path, 'POST', createKeyBody); 69 | 70 | request(options, function(error) { 71 | if (error) console.log('Error creating cloud account connection', error); 72 | else console.log("finished creating cloud account connection"); 73 | kcb(); 74 | }); 75 | }; 76 | var createIAMRolePolcy = function(generatedID, credentials, callback) { 77 | let iam = new AWS.IAM(credentials); 78 | 79 | let aquaCSPMAssumeRole = { 80 | "Version": "2008-10-17", 81 | "Statement": [ 82 | { 83 | "Effect": "Allow", 84 | "Principal": { 85 | "AWS": "arn:aws:iam::057012691312:role/lambda-cloudsploit-api" 86 | }, 87 | "Action": "sts:AssumeRole", 88 | "Condition": { 89 | "StringEquals": { 90 | "sts:ExternalId": `${generatedID}` 91 | }, 92 | "IpAddress": { 93 | "aws:SourceIp": "3.231.74.65/32" 94 | } 95 | } 96 | }, 97 | { 98 | "Effect": "Allow", 99 | "Principal": { 100 | "AWS": "arn:aws:iam::057012691312:role/lambda-cloudsploit-collector" 101 | }, 102 | "Action": "sts:AssumeRole", 103 | "Condition": { 104 | "StringEquals": { 105 | "sts:ExternalId": `${generatedID}` 106 | }, 107 | "IpAddress": { 108 | "aws:SourceIp": "3.231.74.65/32" 109 | } 110 | } 111 | }, 112 | { 113 | "Effect": "Allow", 114 | "Principal": { 115 | "AWS": "arn:aws:iam::057012691312:role/lambda-cloudsploit-remediator" 116 | }, 117 | "Action": "sts:AssumeRole", 118 | "Condition": { 119 | "StringEquals": { 120 | "sts:ExternalId": `${generatedID}` 121 | }, 122 | "IpAddress": { 123 | "aws:SourceIp": "3.231.74.65/32" 124 | } 125 | } 126 | }, 127 | { 128 | "Effect": "Allow", 129 | "Principal": { 130 | "AWS": "arn:aws:iam::057012691312:role/lambda-cloudsploit-tasks" 131 | }, 132 | "Action": "sts:AssumeRole", 133 | "Condition": { 134 | "StringEquals": { 135 | "sts:ExternalId": `${generatedID}` 136 | }, 137 | "IpAddress": { 138 | "aws:SourceIp": "3.231.74.65/32" 139 | } 140 | } 141 | } 142 | ] 143 | }; 144 | 145 | let aquaCSPMAssumePolicy = { 146 | "Version": "2012-10-17", 147 | "Statement": [ 148 | { 149 | "Effect": "Allow", 150 | "Action": [ 151 | "ses:DescribeActiveReceiptRuleSet", 152 | "athena:GetWorkGroup", 153 | "logs:DescribeLogGroups", 154 | "logs:DescribeMetricFilters", 155 | "config:getComplianceDetailsByConfigRule", 156 | "elastictranscoder:ListPipelines", 157 | "elasticfilesystem:DescribeFileSystems", 158 | "servicequotas:ListServiceQuotas", 159 | "ssm:ListAssociations", 160 | "dlm:GetLifecyclePolicies", 161 | "airflow:ListEnvironments", 162 | "glue:GetSecurityConfigurations", 163 | "devops-guru:ListNotificationChannels", 164 | "ec2:GetEbsEncryptionByDefault", 165 | "ec2:GetEbsDefaultKmsKeyId", 166 | "organizations:ListAccounts", 167 | "kendra:ListIndices", 168 | "proton:ListEnvironmentTemplates", 169 | "qldb:ListLedgers", 170 | "airflow:ListEnvironments", 171 | "profile:ListDomains", 172 | "timestream:DescribeEndpoints", 173 | "timestream:ListDatabases", 174 | "frauddetector:GetDetectors", 175 | "memorydb:DescribeClusters", 176 | "kafka:ListClusters", 177 | "apprunner:ListServices", 178 | "finspace:ListEnvironments", 179 | "healthlake:ListFHIRDatastores", 180 | "codeartifact:ListDomains", 181 | "auditmanager:GetSettings", 182 | "appflow:ListFlows", 183 | "databrew:ListJobs", 184 | "managedblockchain:ListNetworks", 185 | "connect:ListInstances", 186 | "backup:ListBackupVaults", 187 | "backup:DescribeRegionSettings", 188 | "backup:getBackupVaultNotifications", 189 | "backup:ListBackupPlans", 190 | "dlm:GetLifecyclePolicies", 191 | "glue:GetSecurityConfigurations", 192 | "ssm:describeSessions", 193 | "ssm:GetServiceSetting", 194 | "ecr:DescribeRegistry", 195 | "ecr-public:DescribeRegistries", 196 | "kinesisvideo:ListStreams", 197 | "wisdom:ListAssistants", 198 | "voiceid:ListDomains", 199 | "lookoutequipment:ListDatasets", 200 | "iotsitewise:DescribeDefaultEncryptionConfiguration", 201 | "geo:ListTrackers", 202 | "geo:ListGeofenceCollections", 203 | "lookoutvision:ListProjects", 204 | "lookoutmetrics:ListAnomalyDetectors", 205 | "lex:ListBots", 206 | "forecast:ListDatasets", 207 | "forecast:ListForecastExportJobs", 208 | "forecast:DescribeDataset", 209 | "lambda:GetFunctionUrlConfig" 210 | ], 211 | "Resource": "*" 212 | } 213 | ] 214 | }; 215 | 216 | let policyARN; 217 | 218 | let roleArn; 219 | 220 | async.series([ 221 | function(pcb) { 222 | iam.createPolicy({ 223 | PolicyDocument: JSON.stringify(aquaCSPMAssumePolicy), 224 | PolicyName: policyName 225 | }, function(policyErr, policyData) { 226 | if (policyErr) { 227 | console.log(policyErr); 228 | return pcb(policyErr); 229 | } 230 | 231 | if (policyData && policyData.Policy && policyData.Policy.Arn) { 232 | policyARN = policyData.Policy.Arn; 233 | } 234 | 235 | pcb(); 236 | }); 237 | }, 238 | function(rcb) { 239 | iam.createRole({ 240 | AssumeRolePolicyDocument: JSON.stringify(aquaCSPMAssumeRole), 241 | RoleName: roleName, 242 | PermissionsBoundary: `${policyARN}` 243 | }, function(roleErr, roleData) { 244 | if (roleErr) { 245 | console.log(roleErr); 246 | return rcb(roleErr); 247 | } 248 | 249 | if (roleData && roleData.Role && roleData.Role.Arn) { 250 | roleArn = roleData.Role.Arn; 251 | } 252 | 253 | rcb(); 254 | }); 255 | }, 256 | function(acb) { 257 | iam.attachRolePolicy({ 258 | PolicyArn: 'arn:aws:iam::aws:policy/SecurityAudit', 259 | RoleName: roleName 260 | }, function(attachErr, attachData) { 261 | if (attachErr) { 262 | console.log (attachErr); 263 | return acb(); 264 | } 265 | 266 | console.log(attachData); 267 | 268 | acb(); 269 | }); 270 | } 271 | ], function(err) { 272 | callback(err, roleArn); 273 | }); 274 | }; 275 | 276 | 277 | 278 | let generatedIDArr = []; 279 | let groupId; 280 | 281 | async.series([ 282 | // collects the necessary generated id's needed to create the proper connections 283 | function(gicb) { 284 | let path = '/v2/generatedids'; 285 | let generatedIdBody = JSON.stringify({count: Object.keys(cloudAccounts).length}); 286 | 287 | let options = setRequestOptions(path, 'POST', generatedIdBody); 288 | 289 | request(options, function(error, response, body){ 290 | if (error) return console.log(error); 291 | 292 | let parsedBody = JSON.parse(body); 293 | 294 | generatedIDArr = JSON.parse(JSON.stringify(parsedBody.data)); 295 | 296 | gicb(); 297 | }); 298 | }, 299 | // collects the group to assign to the key 300 | function(gcb) { 301 | let path = '/v2/groups'; 302 | let options = setRequestOptions(path, 'GET', ''); 303 | 304 | request(options, function(error, response, body){ 305 | if (error) return console.log(error); 306 | 307 | let parsedBody = JSON.parse(body); 308 | 309 | let groupArr = JSON.parse(JSON.stringify(parsedBody.data)); 310 | let groupName = groupToAdd || 'Default'; 311 | 312 | let foundGroup = groupArr.find(group => { return group.name === groupName; }); 313 | 314 | groupId = foundGroup.id; 315 | gcb(); 316 | }); 317 | }, 318 | // creates a new role and key 319 | function(icb) { 320 | async.eachOfLimit(cloudAccounts, 1, function(credentials, keyName, cacb) { 321 | let generatedIDObj = generatedIDArr.pop(); 322 | let generatedID = generatedIDObj.generated_id; 323 | 324 | createIAMRolePolcy(generatedID, credentials, function(err, iamRole) { 325 | let keyBody = { 326 | role_arn: iamRole, 327 | external_id: generatedID, 328 | name: keyName, 329 | group_id: groupId 330 | }; 331 | 332 | addNewKey(JSON.stringify(keyBody), cacb); 333 | }); 334 | }, function() { 335 | icb(); 336 | }); 337 | } 338 | ], function() { 339 | console.log('Finished'); 340 | }); --------------------------------------------------------------------------------