Bitbucket Symfony Security Checker

To implement the symfony security checker as part of your bitbucket pipeline, include the following lines of code in one of the steps.

The first line downloads the Symfony CLI tool, and adds it to the path (so it can be run). The 2nd line actually runs the security-check.

- curl -sS https://get.symfony.com/cli/installer | bash && export PATH="$HOME/.symfony/bin:$PATH"
- symfony check:security

Ref; https://symfony.com/doc/current/setup.html#checking-security-vulnerabilities

Import/Export data from DynamoDB (small amounts)

I use this CLI command for exporting and then later importing data from/to DynamoDB (small amounts of data – eg. upto a few hundred items).

It does take a while to get data back into DynamoDB, as it’s doing it line-by-line, rather than as a batch … but gets the job done!

Export;

aws dynamodb scan --table-name source-table-name --no-paginate > data.json

Import;

cat data.json | jq -c '.Items[]' | while read -r line; do aws dynamodb put-item --table-name destination-table-name --item "$line"; done

This can be done in one line as well;

aws dynamodb scan --table-name source-table-name --no-paginate | jq -c '.Items[]' | while read -r line; do aws dynamodb put-item --table-name destination-table-name --item "$line"; done

Credit goes to; https://github.com/guillaumesmo

Cleaning up old DynamoDB Auto-Scaling Resources

I’ve found a strange problem with cloud-formation roll-backs which don’t automatically remove any Auto Scaling resources you might have setup.

This means then when you next deploy, CloudFormation starts complaining about resources already existing!

To clean these up, you need the following (run from the CLI, using the AWS CLI);

List resources;

aws application-autoscaling describe-scalable-targets --service-namespace dynamodb

From there, de-register (remove) each of the ones which shouldn’t be there;

aws application-autoscaling deregister-scalable-target --service-namespace dynamodb --resource-id "table/myTableName" --scalable-dimension "dynamodb:table:ReadCapacityUnits"

aws application-autoscaling deregister-scalable-target --service-namespace dynamodb --resource-id "table/myTableName" --scalable-dimension "dynamodb:table:WriteCapacityUnits"

That’s it!

AWS Invoking Lambda functions from CLI

aws lambda invoke \
  --function-name <your function name> \
  --payload '"<your json payload>"' \ 
  --cli-binary-format raw-in-base64-out /dev/stdout

The raw-in-base64-out lets you skip having to base64 encode the payload.

The /dev/stdout bit at the end just shows the output on your screen, rather than outputting it to a file and then having to read that file.

Ref;

  • https://docs.aws.amazon.com/cli/latest/reference/lambda/invoke.html
  • https://acloud.guru/forums/aws-lambda/discussion/-Lys1N6wVQCHE6Ucoxvt/getting-error-as-invalid-base64-for-the-same-data-provided-for-the-kinesis-lectu?answer=-M0gBMHWlAYMm9z1cv6i
  • https://stackoverflow.com/questions/47675032/invoking-aws-lambda-without-output-file

Unit-testing Bref lambda handlers

Hopefully this helps someone out there unit-testing Bref lambda consumers (eg. AWS lambda handlers for SNS / EventBridge / SQS, etc) with PHPUnit;

Essentially this includes the consumer (which is essentially a PHP function), and calls the function with array of event-data (in the same format AWS would normally give it).

The function (handler) would then return a response (hopefully with no thrown errors), and any unit-testing on the result would be done.

public function testConsumeUpdatePerson() {
        $handler = include(__DIR__ . '/../bin/consume');

        $data = json_encode([
            'action' => 'update-person',
            'id' => 1234
        ]);
        $overallJson = '{
  "Records": [
    {
      "EventVersion": "1.0",
      "EventSubscriptionArn": "arn:aws:sns:us-east-2:123456678:sns-lambda:abc-123",
      "EventSource": "aws:sns",
      "Sns": {
        "SignatureVersion": "1",
        "Timestamp": "2019-01-02T12:45:07.000Z",
        "Signature": "aaaabbbb/ccccdddd/111111==",
        "SigningCertUrl": "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-aaaabbbb.pem",
        "MessageId": "aaaabbbbb",
        "Message": "' . addslashes($data) . '",
        "MessageAttributes": {
          "Test": {
            "Type": "String",
            "Value": "TestString"
          },
          "TestBinary": {
            "Type": "Binary",
            "Value": "TestBinary"
          }
        },
        "Type": "Notification",
        "UnsubscribeUrl": "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&amp;SubscriptionArn=arn:aws:sns:us-east-2:111122222:test-lambda:aaaaa-bbbbb",
        "TopicArn" : "arn:aws:sns:ap-southeast-2:1111222222:topic-name-goes-here",
        "Subject": "TestInvoke"
      }
    }
  ]
}';
        $event = json_decode($overallJson, true);

        $response = $handler($event, new Context('', 300, '', ''));
        $this->assertEquals('OK', $response);
    }

More unit-tests can obviously be added below, but the basics of this test that there’s no errors, unhandled exceptions, etc which you hadn’t fully tested otherwise

Serializer – array to Object

To convert from an array (including multi-dimensional arrays) to an object, the following code might help!

Ref; https://symfony.com/doc/current/components/serializer.html

// all callback parameters are optional (you can omit the ones you don't use)
$extractor = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]);
$normalizer = new ObjectNormalizer(null, null, null, $extractor);
$serializer = new Serializer(
    [
        $normalizer,
        new ArrayDenormalizer(),
    ]
);

$obj = $serializer->denormalize(
    $data,
    Member::class,
    null,
    [ObjectNormalizer::DISABLE_TYPE_ENFORCEMENT => true]
);

Symfony

This can also be achieved by installing the following packages, which Symfony will pickup & use with it’s serializer;

composer require phpdocumentor/reflection-docblock
composer require symfony/property-info

Then, in your service (or controller);

public function __construct(SerializerInterface $serializer) {
    $this->serializer = $serializer;
}

And in your code;

$obj = $this->serializer->denormalize(
    $rowData,
    Member::class
);

You can also lessen the enforcement of variable-types (strings vs ints) with the following;

$obj = $serializer->denormalize(
    $data,
    Member::class,
    null,
    [ObjectNormalizer::DISABLE_TYPE_ENFORCEMENT => true]
);

Ref; https://symfony.com/doc/current/components/property_info.html

Serverless – creating DNS entries for API Gateway

The following can be included in your serverless.yml file to create a sub-domain in Route53, and link it upto your API Gateway (HTTP) endpoint.

The following variables are required in your ‘custom’ block in the serverless.yml file;

  • certificate_arn – this is the ARN of your AWS Certificate Manager SSL certificate. For regional endpoints, this should be a cert created in the same region as your API Gateway.
  • domain_hosted_zone – the zone name of your domain name (eg. if your subdomain you want is abcd.myexample.com, the domain_hosted_zone will be example.com
  • domain_name – this is the complete sub-domain (eg. abcd.myexample.com)

Eg;

custom:
  domain_hosted_zone: 'example.com.'
  domain_name: 'abcd.example.com'
  certificate_arn: 'arn:aws:acm:ap-southeast-2:1233456:certificate/abc123'
resources:
  Resources:
    APIDomainName:
      Type: 'AWS::ApiGatewayV2::DomainName'
      Properties:
        DomainNameConfigurations:
          - CertificateArn: ${self:custom.certificate_arn}
        DomainName: ${self:custom.domain_name}

    APIDomainMapping:
      Type: 'AWS::ApiGatewayV2::ApiMapping'
      Properties:
        ApiId: !Ref HttpApi
        DomainName: !Ref APIDomainName
        Stage: !Ref HttpApiStage
      DependsOn: [ APIDomainName ]

    APIDomain:
      Type: AWS::Route53::RecordSetGroup
      Properties:
        HostedZoneName: ${self:custom.domain_hosted_zone}
        RecordSets:
          - Name: !Ref APIDomainName
            Type: A
            AliasTarget:
              DNSName: !GetAtt APIDomainName.RegionalDomainName
              HostedZoneId: !GetAtt APIDomainName.RegionalHostedZoneId

Ref; https://theburningmonk.com/cloudformation-ref-and-getatt-cheatsheet/

Chocolate melting mini-cakes

Ingredients

  • 3/4 cup dark chocolate chips
  • 3/4 cup butter
  • 4 eggs (room temperature)
  • 3/4 cup sugar
  • 1/8 teaspoon vanilla extract
  • 1/4 cup flour

Instructions

  1. Preheat oven to 190 degrees c
  2. Melt chocolate and butter in a small saucepan, cool 10 minutes.
  3. In the meantime, in a separate bowl, whisk eggs and sugar together.
  4. Add vanilla extract/essence
  5. Add flour and whisk until flour is well mixed in.
  6. When chocolate has cooled stir in egg mixture.
  7. Fill 7 oz. ramekins about 3/4 of the way full with chocolate batter.
  8. Bake for 15-20 mins (don’t go overboard or it’ll be huge!)

The cake should be cake spongy on the top but the middle of the cake should be melty and gooey-the consistency of pudding, not too runny.
Do not let it overcook. Watch these babies closely!
Serve with ice cream or whipped cream.

Credit goes to: https://www.favfamilyrecipes.com/melting-cake/