├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.sbt ├── project ├── assembly.sbt └── build.properties ├── sbt ├── sbt-launch.jar └── src ├── main └── scala │ └── com │ └── gu │ └── pentest │ ├── Args.scala │ ├── Aws.scala │ ├── Main.scala │ ├── Output.scala │ ├── model.scala │ └── util │ └── AwsTools.scala └── test └── scala └── com └── gu └── pentest ├── AwsTest.scala └── MainTest.scala /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### SBT ### 4 | # Simple Build Tool 5 | # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control 6 | 7 | target/ 8 | lib_managed/ 9 | src_managed/ 10 | project/boot/ 11 | .history 12 | .cache 13 | 14 | 15 | ### Scala ### 16 | *.class 17 | *.log 18 | 19 | # sbt specific 20 | .cache 21 | .history 22 | .lib/ 23 | dist/* 24 | target/ 25 | lib_managed/ 26 | src_managed/ 27 | project/boot/ 28 | project/plugins/project/ 29 | 30 | # Scala-IDE specific 31 | .scala_dependencies 32 | .worksheet 33 | 34 | 35 | ### Emacs ### 36 | # -*- mode: gitignore; -*- 37 | *~ 38 | \#*\# 39 | /.emacs.desktop 40 | /.emacs.desktop.lock 41 | *.elc 42 | auto-save-list 43 | tramp 44 | .\#* 45 | 46 | # Org-mode 47 | .org-id-locations 48 | *_archive 49 | 50 | # flymake-mode 51 | *_flymake.* 52 | 53 | # eshell files 54 | /eshell/history 55 | /eshell/lastdir 56 | 57 | # elpa packages 58 | /elpa/ 59 | 60 | # reftex files 61 | *.rel 62 | 63 | # AUCTeX auto folder 64 | /auto/ 65 | 66 | # cask packages 67 | .cask/ 68 | 69 | 70 | ### Intellij ### 71 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 72 | 73 | *.iml 74 | 75 | ## Directory-based project format: 76 | .idea/ 77 | # if you remove the above rule, at least ignore the following: 78 | 79 | # User-specific stuff: 80 | # .idea/workspace.xml 81 | # .idea/tasks.xml 82 | # .idea/dictionaries 83 | 84 | # Sensitive or high-churn files: 85 | # .idea/dataSources.ids 86 | # .idea/dataSources.xml 87 | # .idea/sqlDataSources.xml 88 | # .idea/dynamic.xml 89 | # .idea/uiDesigner.xml 90 | 91 | # Gradle: 92 | # .idea/gradle.xml 93 | # .idea/libraries 94 | 95 | # Mongo Explorer plugin: 96 | # .idea/mongoSettings.xml 97 | 98 | ## File-based project format: 99 | *.ipr 100 | *.iws 101 | 102 | ## Plugin-specific files: 103 | 104 | # IntelliJ 105 | /out/ 106 | 107 | # mpeltonen/sbt-idea plugin 108 | .idea_modules/ 109 | 110 | # JIRA plugin 111 | atlassian-ide-plugin.xml 112 | 113 | # Crashlytics plugin (for Android Studio and IntelliJ) 114 | com_crashlytics_export_strings.xml 115 | crashlytics.properties 116 | crashlytics-build.properties 117 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v1.2 Use private IPs and warn about excluded instances 4 | 5 | We now use private IP addresses for instances that do not have a 6 | public IP address attached. 7 | 8 | This also includes an update to the unsupported instances 9 | behaviour. We now exclude the new t2.nano type and display an error 10 | describing which (if any) instances have been excluded from the 11 | pen-test request. 12 | 13 | ## v1.1 Exclude unsupported instance types 14 | 15 | As mentioned on 16 | [the AWS information page](http://aws.amazon.com/security/penetration-testing/), 17 | certain instance types are not supported for penetration testing. We 18 | now exclude these instances from the information produced by this 19 | tool. 20 | 21 | ## v1.0 Initial release 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 Guardian News & Media Ltd. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AWS Pen Test Form 2 | ================= 3 | 4 | This project is a tool to help automate filling in the 5 | [AWS penetration testing form](https://portal.aws.amazon.com/gp/aws/html-forms-controller/contactus/AWSSecurityPenTestRequest). 6 | 7 | Before commencing penetration testing on assets in AWS, the form must 8 | be completed to request permission from Amazon. In the modern world of 9 | autoscaled instances the form appears, at first glance, to be fairly 10 | unhelpful. In these cases Amazon recommend listing all current 11 | instances as normal, btu adding a section in the comments that 12 | describes each autoscaling group and the instances current;y running 13 | therein. This helps them to apply the permission to the AS groups 14 | rather than to specific instances. 15 | 16 | Filling in the form properly is tedious, especially when you need to 17 | exclude instance types that are not allowed to be pen tested. This 18 | tool uses the AWS SDK to generate that information for you. 19 | 20 | ## Usage 21 | 22 | In 23 | [the releases section of this repo](https://github.com/guardian/aws-pen-test-form/releases) 24 | is a jar that can be executed directly. 25 | 26 | java -jar pen-test-form.jar [] 27 | 28 | e.g. 29 | 30 | java -jar pen-test-form.jar my-profile eu-west-1 31 | 32 | The project uses the AWS credentials file so you should provide a 33 | profile to choose which account to use (just as you would when using 34 | the AWS CLI tool). More info is available in the 35 | [AWS CLI documentation](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html). 36 | Note that the credentials attached to this profile will need at 37 | least the following permissions: 38 | 39 | * `autoscaling:DescribeAutoScalingGroups` 40 | * `ec2:DescribeInstances` 41 | 42 | The second argument is the AWS region to use when checking for AS 43 | groups and instances. This defaults to eu-west-1 for our (The 44 | Guardian's) convenience. 45 | 46 | ## Output 47 | 48 | The tool will output the details as text that you can copy into the 49 | pen test form. It will look a little like the following: 50 | 51 | Instance IDs: 52 | i-21fedcba 53 | i-abcdef12 54 | 55 | Instance IPs: 56 | 1.1.1.1 57 | 1.1.1.2 58 | 59 | Comments: 60 | Here are full details of the AS groups and current instances. The actual instances will change during routine scaling and deployments. 61 | 62 | my-AutoscalingGroup-123456789 63 | 1.1.1.1 i-21fedcba (t2.medium) 64 | 1.1.1.2 i-abcdef12 (t2.medium) 65 | 66 | These are the three form fields that are tedious to fill in. You'll 67 | still need to get the details for the other fields. 68 | 69 | ### Invalid instances 70 | 71 | AWS does not allow penetration testing to be performed on t1.small, 72 | t1.medium or t2.nano instance types. If you attempt to submit a request 73 | when these instance types are in use you'll be presented with a warning. 74 | This will describe which AutoScaling Groups are affected and which 75 | instances caused the problem. The affected instances will nto be 76 | included in the request and any AutoScaling Groups that have no valid 77 | instances will also be skipped. 78 | 79 | ## Development / alternate usage 80 | 81 | You can also check out the project and run it directly using 82 | `sbt`. Open a command line from within the project and execute the 83 | following: 84 | 85 | ./sbt "run []" 86 | 87 | This will fetch all the dependencies, compile the project and execute 88 | it the same way as running the jar directly would. 89 | 90 | ### Building 91 | 92 | You can build your own jar by running the `assembly` command. 93 | 94 | ./sbt assembly 95 | 96 | This will run the tests and then create a "fat jar" containing all the 97 | project's dependencies. You'll see the location the jar gets saved to 98 | near the bottom of that command's output. 99 | 100 | [info] Packaging /pen-test-form.jar ... 101 | [info] Done packaging. 102 | 103 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | name := """aws-pen-test-form""" 2 | 3 | version := "1.2" 4 | 5 | scalaVersion := "2.11.6" 6 | 7 | libraryDependencies ++= Seq( 8 | "com.amazonaws" % "aws-java-sdk" % "1.9.3", 9 | "com.github.scopt" %% "scopt" % "3.3.0", 10 | "joda-time" % "joda-time" % "2.9.4", 11 | "org.joda" % "joda-convert" % "1.8", 12 | "org.scalatest" %% "scalatest" % "2.2.4" % "test" 13 | ) 14 | 15 | assemblyJarName in assembly := "pen-test-form.jar" 16 | 17 | scalacOptions := Seq("-unchecked", "-deprecation") 18 | -------------------------------------------------------------------------------- /project/assembly.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.13.0") 2 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.8 2 | -------------------------------------------------------------------------------- /sbt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Debug option 4 | DEBUG_PARAMS="" 5 | for arg in "$@" 6 | do 7 | if [ "$arg" == "--debug" ]; then 8 | echo "setting java process as debuggable" 9 | DEBUG_PARAMS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1057" 10 | shift 11 | fi 12 | done 13 | 14 | java $DEBUG_PARAMS \ 15 | -Xms1024M -Xmx2048M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=1024M \ 16 | -jar sbt-launch.jar "$@" 17 | -------------------------------------------------------------------------------- /sbt-launch.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guardian/aws-pen-test-form/4b70b32bb799613d9e21c9572900540a911c44e5/sbt-launch.jar -------------------------------------------------------------------------------- /src/main/scala/com/gu/pentest/Args.scala: -------------------------------------------------------------------------------- 1 | package com.gu.pentest 2 | 3 | import com.amazonaws.regions.{Regions, Region} 4 | 5 | case class Arguments(profile: String = "", region: Region = Region.getRegion(Regions.EU_WEST_1)) 6 | -------------------------------------------------------------------------------- /src/main/scala/com/gu/pentest/Aws.scala: -------------------------------------------------------------------------------- 1 | package com.gu.pentest 2 | 3 | import java.util.{Timer, TimerTask} 4 | 5 | import com.amazonaws.auth.AWSCredentials 6 | import com.amazonaws.auth.profile.ProfileCredentialsProvider 7 | import com.amazonaws.regions.Region 8 | import com.amazonaws.services.autoscaling.AmazonAutoScalingAsyncClient 9 | import com.amazonaws.services.autoscaling.model.{DescribeAutoScalingGroupsRequest, DescribeAutoScalingGroupsResult} 10 | import com.amazonaws.services.ec2.AmazonEC2AsyncClient 11 | import com.amazonaws.services.ec2.model.{DescribeInstancesRequest, DescribeInstancesResult} 12 | import com.gu.pentest.util.AwsTools 13 | 14 | import scala.collection.JavaConversions._ 15 | import scala.concurrent.duration.Duration 16 | import scala.concurrent.{Future, Promise} 17 | import scala.util.Success 18 | 19 | 20 | object Aws { 21 | def getCredentials(profileName: String): AWSCredentials = 22 | new ProfileCredentialsProvider(profileName).getCredentials 23 | 24 | // EC2 25 | 26 | def ec2Client(credentials: AWSCredentials, region: Region): AmazonEC2AsyncClient = { 27 | val client = new AmazonEC2AsyncClient(credentials) 28 | client.setRegion(region) 29 | client 30 | } 31 | 32 | def describeInstances(ec2Client: AmazonEC2AsyncClient, request: DescribeInstancesRequest = new DescribeInstancesRequest() 33 | ): Future[DescribeInstancesResult] = { 34 | AwsTools.asFuture(ec2Client.describeInstancesAsync)(request) 35 | } 36 | 37 | // Autoscaling 38 | 39 | def asClient(credentials: AWSCredentials, region: Region): AmazonAutoScalingAsyncClient = { 40 | val client = new AmazonAutoScalingAsyncClient(credentials) 41 | client.setRegion(region) 42 | client 43 | } 44 | 45 | def describeAutoScalingGroups(asClient: AmazonAutoScalingAsyncClient, request: DescribeAutoScalingGroupsRequest = new DescribeAutoScalingGroupsRequest): Future[DescribeAutoScalingGroupsResult] = { 46 | AwsTools.asFuture(asClient.describeAutoScalingGroupsAsync)(request) 47 | } 48 | 49 | // Tools 50 | val ineligbleInstanceTypes = Set("t1.micro", "m1.small", "t2.nano") 51 | 52 | def getInstances(instancesResult: DescribeInstancesResult): List[PenTestInstance] = { 53 | instancesResult.getReservations.toList.flatMap { reservation => 54 | reservation.getInstances.toList.flatMap { instance => 55 | for { 56 | id <- Option(instance.getInstanceId) 57 | ip <- Option(instance.getPublicIpAddress).orElse(Option(instance.getPrivateIpAddress)) 58 | instanceType <- Option(instance.getInstanceType) 59 | } yield PenTestInstance(id, ip, instanceType) 60 | } 61 | } 62 | } 63 | 64 | def getAsGroups(asGroupsResult: DescribeAutoScalingGroupsResult): List[PenTestAsGroup] = { 65 | asGroupsResult.getAutoScalingGroups.toList.map { asGroup => 66 | val name = asGroup.getAutoScalingGroupName 67 | val instanceIds = asGroup.getInstances.toList.map(_.getInstanceId) 68 | PenTestAsGroup(name, instanceIds) 69 | } 70 | } 71 | 72 | def getPenTestDetails(asGroups: List[PenTestAsGroup], instances: List[PenTestInstance]): Map[String, List[PenTestInstance]] = { 73 | val validInstances = instances.filter(validInstanceType) 74 | val validInstanceIds = validInstances.map(_.id) 75 | 76 | (for { 77 | asGroup <- asGroups 78 | if asGroup.instanceIds.exists(validInstanceIds.contains) 79 | instanceId <- asGroup.instanceIds 80 | validAsGroupInstances = validInstances.filter(instance => asGroup.instanceIds.contains(instance.id)) 81 | } yield asGroup.name -> validAsGroupInstances).toMap 82 | } 83 | 84 | def warnings(asGroups: List[PenTestAsGroup], instances: List[PenTestInstance]): Map[PenTestAsGroup, List[PenTestInstance]] = { 85 | val invalidInstances = instances.filterNot(validInstanceType) 86 | val invalidInstanceIds = invalidInstances.map(_.id) 87 | 88 | (for { 89 | asGroup <- asGroups 90 | if asGroup.instanceIds.exists(invalidInstanceIds.contains) 91 | } yield { 92 | asGroup -> invalidInstances.filter(instance => asGroup.instanceIds.contains(instance.id)) 93 | }).toMap 94 | } 95 | 96 | def validInstanceType(instance: PenTestInstance): Boolean = { 97 | !ineligbleInstanceTypes.contains(instance.instanceType) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/scala/com/gu/pentest/Main.scala: -------------------------------------------------------------------------------- 1 | package com.gu.pentest 2 | 3 | import com.amazonaws.regions.{Region, Regions} 4 | import scopt.OptionParser 5 | 6 | import scala.concurrent.ExecutionContext.Implicits.global 7 | 8 | 9 | object Main { 10 | def main(args: Array[String]): Unit = { 11 | argParser.parse(args, Arguments()) match { 12 | case Some(Arguments(profile, region)) => 13 | val credentials = Aws.getCredentials(profile) 14 | val ec2Client = Aws.ec2Client(credentials, region) 15 | val asClient = Aws.asClient(credentials, region) 16 | 17 | // kick AWS requests off in parallel 18 | val fInstancesResult = Aws.describeInstances(ec2Client) 19 | val fasGroupsResult = Aws.describeAutoScalingGroups(asClient) 20 | 21 | for { 22 | instancesResult <- fInstancesResult 23 | asGroupsResult <- fasGroupsResult 24 | instances = Aws.getInstances(instancesResult) 25 | asGroups = Aws.getAsGroups(asGroupsResult) 26 | details = Aws.getPenTestDetails(asGroups, instances) 27 | output = Output.detailsAsString(details) 28 | warningsDetails = Aws.warnings(asGroups, instances) 29 | warningOutput = Output.warningsAsString(warningsDetails) 30 | } { 31 | println(output) 32 | System.err.println(Console.YELLOW + warningOutput + Console.RESET) 33 | System.exit(0) 34 | } 35 | 36 | case _ => 37 | // parsing cmd line args failed, help message will be displayed 38 | System.exit(1) 39 | } 40 | } 41 | 42 | val argParser = new OptionParser[Arguments]("pen-test-form") { 43 | arg[String]("profile") action { (profile, arguments) => 44 | arguments.copy(profile = profile) 45 | } text "AWS account profile name" 46 | arg[String]("region") optional() validate { region => 47 | try { 48 | Region.getRegion(Regions.fromName(region)) 49 | success 50 | } catch { 51 | case e: IllegalArgumentException => 52 | failure(s"Invalid AWS region name, $region") 53 | } 54 | } action { (region, arguments) => 55 | arguments.copy(region = Region.getRegion(Regions.fromName(region))) 56 | } text "AWS region name (defaults to eu-west-1)" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/scala/com/gu/pentest/Output.scala: -------------------------------------------------------------------------------- 1 | package com.gu.pentest 2 | 3 | import org.joda.time.DateTime 4 | import org.joda.time.format.DateTimeFormat 5 | 6 | object Output { 7 | def warningsAsString(details: Map[PenTestAsGroup, List[PenTestInstance]]): String = { 8 | if (details.isEmpty) "" 9 | else { 10 | val asGroupsText = details.toList.map { case (asGroup, instances) => 11 | s"""AutoScaling Group: 12 | |${asGroup.name} 13 | |Instances: 14 | |${instances.map(instanceWarning).mkString("\n")}""".stripMargin 15 | } mkString "\n\n" 16 | 17 | s"""Some AutoScaling groups contain instances that are not eligible for pen testing. 18 | |AWS does not allow pen-testing on the following instance types: 19 | |${Aws.ineligbleInstanceTypes.mkString(", ")} 20 | | 21 | |You should either temporarily change these instances to allowed instance types 22 | |or be aware that these will be out-of-scope for the pen-test. 23 | | 24 | |$asGroupsText""".stripMargin 25 | } 26 | } 27 | 28 | private def instanceWarning(instance: PenTestInstance): String = { 29 | s"${instance.id} of type ${instance.instanceType}" 30 | } 31 | 32 | def detailsAsString(details: Map[String, List[PenTestInstance]]): String = { 33 | val instanceIps = details.values.flatten.map(_.ip).mkString("\n") 34 | val instanceIds = details.values.flatten.map(_.id).mkString("\n") 35 | 36 | val comments = details.map { case (asGroup, instances) => 37 | val asInstances = instances.map(i => s"${i.id}\t${i.ip} (${i.instanceType})").mkString("\n") 38 | s"$asGroup\n$asInstances" 39 | } mkString "\n\n" 40 | 41 | val future = DateTime.now().plusDays(90) 42 | val format = DateTimeFormat.forPattern("yyyy-MM-dd") 43 | 44 | s"""Instance IDs: 45 | |$instanceIds 46 | | 47 | |Instance IPs: 48 | |$instanceIps 49 | | 50 | |Comments: 51 | |Here are full details of the AS groups and current instances. 52 | |The actual instances will change during routine scaling and deployments. 53 | | 54 | |$comments 55 | | 56 | |End date: 57 | |${format.print(future)} 00:00 58 | """.stripMargin 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/scala/com/gu/pentest/model.scala: -------------------------------------------------------------------------------- 1 | package com.gu.pentest 2 | 3 | case class PenTestInstance(id: String, ip: String, instanceType: String) 4 | 5 | case class PenTestAsGroup(name: String, instanceIds: List[String]) 6 | -------------------------------------------------------------------------------- /src/main/scala/com/gu/pentest/util/AwsTools.scala: -------------------------------------------------------------------------------- 1 | package com.gu.pentest.util 2 | 3 | import com.amazonaws.AmazonWebServiceRequest 4 | import com.amazonaws.handlers.AsyncHandler 5 | 6 | import scala.concurrent.{Future, Promise} 7 | 8 | object AwsTools { 9 | 10 | class AwsAsyncPromiseHandler[R <: AmazonWebServiceRequest, T](promise: Promise[T]) extends AsyncHandler[R, T] { 11 | def onError(e: Exception) = promise failure e 12 | def onSuccess(r: R, t: T) = promise success t 13 | } 14 | 15 | def asFuture[R <: AmazonWebServiceRequest, T](awsClientMethod: Function2[R, AsyncHandler[R, T], java.util.concurrent.Future[T]]): Function1[R, Future[T]] = { awsRequest => 16 | val p = Promise[T]() 17 | awsClientMethod(awsRequest, new AwsAsyncPromiseHandler(p)) 18 | p.future 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/scala/com/gu/pentest/AwsTest.scala: -------------------------------------------------------------------------------- 1 | package com.gu.pentest 2 | 3 | import org.scalatest.concurrent.ScalaFutures 4 | import org.scalatest.time.{Millis, Seconds, Span} 5 | import org.scalatest.{BeforeAndAfterAll, FreeSpec, Matchers, OptionValues} 6 | 7 | class AwsTest extends FreeSpec with Matchers with OptionValues with ScalaFutures with BeforeAndAfterAll { 8 | implicit val defaultPatience = 9 | PatienceConfig(timeout = Span(5, Seconds), interval = Span(500, Millis)) 10 | 11 | val instance1 = PenTestInstance("id1", "1.1.1.1", "t2.medium") 12 | val instance2 = PenTestInstance("id2", "1.1.1.2", "t2.medium") 13 | val instance3 = PenTestInstance("id3", "1.1.1.3", "t2.medium") 14 | val nano = PenTestInstance("id-nano", "1.1.1.4", "t2.nano") 15 | val t1Micro = PenTestInstance("id-micro", "1.1.1.5", "t1.micro") 16 | val m1Small = PenTestInstance("id-small", "1.1.1.6", "m1.small") 17 | 18 | "getPenTestDetails" - { 19 | "returns an empty map if there are no AS groups" in { 20 | Aws.getPenTestDetails(Nil, Nil) shouldEqual Map.empty 21 | } 22 | 23 | "works for this example" in { 24 | val instances = List(instance1, instance2, instance3) 25 | val asGroups = List( 26 | PenTestAsGroup("as1", List("id1", "id2")), 27 | PenTestAsGroup("as2", List("id3")) 28 | ) 29 | Aws.getPenTestDetails(asGroups, instances) shouldEqual Map( 30 | "as1" -> List(instance1, instance2), 31 | "as2" -> List(instance3) 32 | ) 33 | } 34 | 35 | "does not include AS groups with no instances" in { 36 | val instances = List(instance1) 37 | val asGroups = List( 38 | PenTestAsGroup("as1", List("id1")), 39 | PenTestAsGroup("as2", List("not-an-instance")) 40 | ) 41 | val result = Aws.getPenTestDetails(asGroups, instances) 42 | result should contain key "as1" 43 | result shouldNot contain key "as2" 44 | } 45 | 46 | "excludes t2.nano instance types" in { 47 | val instances = List(instance1, nano) 48 | val asGroups = List(PenTestAsGroup("as1", instances.map(_.id))) 49 | Aws.getPenTestDetails(asGroups, instances).get("as1").value.map(_.instanceType) should not contain "t2.nano" 50 | } 51 | 52 | "excludes m1.small instance types" in { 53 | val instances = List(instance1, m1Small) 54 | val asGroups = List(PenTestAsGroup("as1", instances.map(_.id))) 55 | Aws.getPenTestDetails(asGroups, instances).get("as1").value.map(_.instanceType) should not contain "m1.small" 56 | } 57 | 58 | "excludes t1.micro instance types" in { 59 | val instances = List(instance1, t1Micro) 60 | val asGroups = List(PenTestAsGroup("as1", instances.map(_.id))) 61 | Aws.getPenTestDetails(asGroups, instances).get("as1").value.map(_.instanceType) should not contain "t1.micro" 62 | } 63 | 64 | "excludes as groups that contain only excluded instance types" in { 65 | val instances = List(m1Small, t1Micro) 66 | val asGroups = List(PenTestAsGroup("as1", instances.map(_.id))) 67 | Aws.getPenTestDetails(asGroups, instances) shouldBe 'empty 68 | } 69 | } 70 | 71 | "warnings" - { 72 | "does not include an AS group with no invalid instances" in { 73 | val instances = List(instance1, instance2) 74 | val asGroups = List(PenTestAsGroup("as1", instances.map(_.id))) 75 | Aws.warnings(asGroups, instances) shouldEqual Map.empty 76 | } 77 | 78 | "given an AS group with invalid instances" - { 79 | val instances = List(instance1, nano, t1Micro) 80 | val asGroup = PenTestAsGroup("as1", instances.map(_.id)) 81 | 82 | "includes that group in its returned groups" in { 83 | Aws.warnings(List(asGroup), instances).keySet.map(_.name) should contain("as1") 84 | } 85 | 86 | "returns all invalid instances for an AS group" in { 87 | val invalidInstances = Aws.warnings(List(asGroup), instances).get(asGroup).value 88 | invalidInstances should contain(nano) 89 | invalidInstances should contain(t1Micro) 90 | } 91 | 92 | "does not include valid instances in the warnings" in { 93 | val invalidInstances = Aws.warnings(List(asGroup), instances).get(asGroup).value 94 | invalidInstances shouldNot contain(instance1) 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/test/scala/com/gu/pentest/MainTest.scala: -------------------------------------------------------------------------------- 1 | package com.gu.pentest 2 | 3 | import com.amazonaws.regions.{Regions, Region} 4 | import org.scalatest.{FreeSpec, Matchers, OptionValues} 5 | 6 | class MainTest extends FreeSpec with Matchers with OptionValues { 7 | "argParser" - { 8 | "correctly parses a profile argument" in { 9 | Main.argParser.parse(Seq("test"), Arguments()).value should have ('profile ("test")) 10 | } 11 | 12 | "fails to parse if the profile argument is missing" in { 13 | Main.argParser.parse(Nil, Arguments()) shouldEqual None 14 | } 15 | 16 | "parses a valid region as the second argument" in { 17 | Main.argParser.parse(Seq("test", "eu-west-1"), Arguments()).value should have ( 18 | 'region (Region.getRegion(Regions.EU_WEST_1)) 19 | ) 20 | } 21 | 22 | "fails if given an invalid AWS region as a second argument" in { 23 | Main.argParser.parse(Seq("test", "not a valid region"), Arguments()) shouldEqual None 24 | } 25 | } 26 | } 27 | --------------------------------------------------------------------------------