<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Samkit Jain on Medium]]></title>
        <description><![CDATA[Stories by Samkit Jain on Medium]]></description>
        <link>https://medium.com/@_samkitjain?source=rss-5523732f685------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*5GX2VbKcv_MvpX8NU2Q1xg.jpeg</url>
            <title>Stories by Samkit Jain on Medium</title>
            <link>https://medium.com/@_samkitjain?source=rss-5523732f685------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 21 Jun 2026 01:58:33 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@_samkitjain/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Minimise the risk of misuse in CI/CD pipeline]]></title>
            <link>https://medium.com/instigence/minimise-the-risk-of-misuse-in-ci-cd-pipeline-600a90ae6985?source=rss-5523732f685------2</link>
            <guid isPermaLink="false">https://medium.com/p/600a90ae6985</guid>
            <category><![CDATA[juvoxa]]></category>
            <category><![CDATA[cicd]]></category>
            <category><![CDATA[github-actions]]></category>
            <category><![CDATA[codebuild]]></category>
            <category><![CDATA[security]]></category>
            <dc:creator><![CDATA[Samkit Jain]]></dc:creator>
            <pubDate>Fri, 19 Nov 2021 11:46:18 GMT</pubDate>
            <atom:updated>2021-11-19T11:46:18.027Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*IqHuKw2w6LuMLKp_" /><figcaption>Photo by <a href="https://unsplash.com/@clemhlrdt?utm_source=medium&amp;utm_medium=referral">Clément Hélardot</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>When having a CI/CD pipeline setup, it is generally configured to be run on commit pushes, pull request creation, etc. Developers are granted access to start the workflow either through customer managed policies or the cloud provider managed policies. It is required that malicious actors don’t misuse the access to tamper with the application and, take control of the servers by executing unwarranted commands using the granted privileges.</p><p>In this article, we will be discussing the steps you can take to minimise this threat. We will be giving examples of GitHub Actions and AWS CodeBuild as the CI/CD pipelines with the resources residing within the AWS ecosystem. The same principles can be applied to other workflows as well.</p><h3>Threats</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*z3tG0BtJ00k22kFf" /><figcaption>Photo by <a href="https://unsplash.com/@flyd2069?utm_source=medium&amp;utm_medium=referral">FLY:D</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><ul><li>Exfiltration of secrets and build artifacts compromising the integrity of the system.</li><li>Injection of malicious commands into the buildspec tampering with the application.</li><li>Taking control of sensitive resources in the system.</li><li>Becoming the weakest link in the application security.</li></ul><h3>Prevention Strategies</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*j9DrfvbdbIzxVP3E" /><figcaption>Photo by <a href="https://unsplash.com/@flyd2069?utm_source=medium&amp;utm_medium=referral">FLY:D</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h4>Be strict with the access</h4><p>When using an AWS IAM role or user in your CI/CD pipeline, be specific with the access it needs. Be as strict as possible and grant the least privileges. For example, if the role needs to download the file from Amazon S3, grant only the s3:GetObject permission instead of granting full access to S3. Even with the read permission, restrict the access to the paths it needs. Instead of granting access to all the files in the bucket, restrict the access to only the files your role needs.</p><p>The <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html">Condition</a> JSON element allows you to provide the least privileges. You can use it to restrict access to requests originating from a certain IP address. An example of a strict policy using the Condition element is</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%2528171%252C%2B184%252C%2B195%252C%2B1%2529%26t%3Dmaterial%26wt%3Dnone%26l%3Dapplication%252Fjson%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3D%25257B%25250A%252520%252520%252520%252520%252522Version%252522%25253A%252520%2525222012-10-17%252522%25252C%25250A%252520%252520%252520%252520%252522Statement%252522%25253A%252520%25255B%25250A%252520%252520%252520%252520%252520%252520%252520%252520%25257B%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522Effect%252522%25253A%252520%252522Allow%252522%25252C%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522Action%252522%25253A%252520%25255B%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522dynamodb%25253AGetItem%252522%25252C%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522dynamodb%25253ABatchGetItem%252522%25252C%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522dynamodb%25253AQuery%252522%25252C%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522dynamodb%25253APutItem%252522%25252C%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522dynamodb%25253AUpdateItem%252522%25252C%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522dynamodb%25253ADeleteItem%252522%25252C%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522dynamodb%25253ABatchWriteItem%252522%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%25255D%25252C%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522Resource%252522%25253A%252520%25255B%252522arn%25253Aaws%25253Adynamodb%25253A%252A%25253A%252A%25253Atable%25252Ftable-name%252522%25255D%25252C%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522Condition%252522%25253A%252520%25257B%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522ForAllValues%25253AStringEquals%252522%25253A%252520%25257B%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522dynamodb%25253AAttributes%252522%25253A%252520%25255B%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522column-name-1%252522%25252C%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522column-name-2%252522%25252C%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522column-name-3%252522%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%25255D%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%25257D%25252C%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252522StringEqualsIfExists%252522%25253A%252520%25257B%252522dynamodb%25253ASelect%252522%25253A%252520%252522SPECIFIC_ATTRIBUTES%252522%25257D%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520%25257D%25250A%252520%252520%252520%252520%252520%252520%252520%252520%25257D%25250A%252520%252520%252520%252520%25255D%25250A%25257D&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%252528171%25252C%2B184%25252C%2B195%25252C%2B1%252529%26t%3Dmaterial%26wt%3Dnone%26l%3Dapplication%25252Fjson%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3D%2525257B%2525250A%25252520%25252520%25252520%25252520%25252522Version%25252522%2525253A%25252520%252525222012-10-17%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252522Statement%25252522%2525253A%25252520%2525255B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%2525257B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522Effect%25252522%2525253A%25252520%25252522Allow%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522Action%25252522%2525253A%25252520%2525255B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522dynamodb%2525253AGetItem%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522dynamodb%2525253ABatchGetItem%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522dynamodb%2525253AQuery%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522dynamodb%2525253APutItem%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522dynamodb%2525253AUpdateItem%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522dynamodb%2525253ADeleteItem%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522dynamodb%2525253ABatchWriteItem%25252522%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%2525255D%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522Resource%25252522%2525253A%25252520%2525255B%25252522arn%2525253Aaws%2525253Adynamodb%2525253A%25252A%2525253A%25252A%2525253Atable%2525252Ftable-name%25252522%2525255D%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522Condition%25252522%2525253A%25252520%2525257B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522ForAllValues%2525253AStringEquals%25252522%2525253A%25252520%2525257B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522dynamodb%2525253AAttributes%25252522%2525253A%25252520%2525255B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522column-name-1%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522column-name-2%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522column-name-3%25252522%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%2525255D%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%2525257D%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252522StringEqualsIfExists%25252522%2525253A%25252520%2525257B%25252522dynamodb%2525253ASelect%25252522%2525253A%25252520%25252522SPECIFIC_ATTRIBUTES%25252522%2525257D%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%2525257D%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%2525257D%2525250A%25252520%25252520%25252520%25252520%2525255D%2525250A%2525257D&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/96eb42a61e3597791086cdcdff268940/href">https://medium.com/media/96eb42a61e3597791086cdcdff268940/href</a></iframe><h4>Do not hardcode secrets</h4><p>Never hardcode the secrets. The secrets can be API keys or IAM user credentials or any other sensitive information. If you are using GitHub Actions, take advantage of <a href="https://docs.github.com/en/actions/security-guides/encrypted-secrets">encrypted secrets</a> to encrypt the secrets and store them securely. If using AWS CodeBuild, take the advantage of AWS Secrets Manager or AWS Systems Manager Parameter Store to load the environment variables securely into the build environment.</p><p>In AWS CodeBuild, you can load sensitive keys from Secrets Manager like below (ensure that the CodeBuild service role has access to fetch and decrypt the secret)</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%2528171%252C%2B184%252C%2B195%252C%2B1%2529%26t%3Dmaterial%26wt%3Dnone%26l%3Dyaml%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dversion%25253A%2525200.2%25250A%25250Aenv%25253A%25250A%252520%252520secrets-manager%25253A%25250A%252520%252520%252520%252520SECRET_API_KEY%25253A%252520arn%25253Aaws%25253Asecretsmanager%25253Aregion%25253Aaccount_id%25253Asecret%25253Asecret_name%25253Aapi_key%25250A%25250Aphases%25253A%25250A%252520%252520...&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%252528171%25252C%2B184%25252C%2B195%25252C%2B1%252529%26t%3Dmaterial%26wt%3Dnone%26l%3Dyaml%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dversion%2525253A%252525200.2%2525250A%2525250Aenv%2525253A%2525250A%25252520%25252520secrets-manager%2525253A%2525250A%25252520%25252520%25252520%25252520SECRET_API_KEY%2525253A%25252520arn%2525253Aaws%2525253Asecretsmanager%2525253Aregion%2525253Aaccount_id%2525253Asecret%2525253Asecret_name%2525253Aapi_key%2525250A%2525250Aphases%2525253A%2525250A%25252520%25252520...&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/32ff6bee092e3a2e19fd93285ae882f9/href">https://medium.com/media/32ff6bee092e3a2e19fd93285ae882f9/href</a></iframe><h4>Diligently do code review</h4><p>Perform proper code review and ensure that no comprising piece of code is getting merged in. Pull requests making changes to the workflow files must be reviewed by the code owners before they can be merged in. That way, any attempt to mess with the deployment servers would be averted.</p><p>Ensure that only trustworthy URLs are accessed to download packages or data.</p><p>On GitHub, you can take advantage of <a href="https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners">CODEOWNERS</a>. For all the CI/CD related files, you can add a code owner so that no change is merged in before explicit approval from a code owner. An example .github/CODEOWNERS may look like the following making the GitHub user username as the owner of all the files in the directory deployment.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%2528171%252C%2B184%252C%2B195%252C%2B1%2529%26t%3Dmaterial%26wt%3Dnone%26l%3Dtext%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3D%25252Fdeployment%25252F%252520%252540username&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%252528171%25252C%2B184%25252C%2B195%25252C%2B1%252529%26t%3Dmaterial%26wt%3Dnone%26l%3Dtext%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3D%2525252Fdeployment%2525252F%25252520%25252540username&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/a596ad9c9259912fb018ae0c3850a3ca/href">https://medium.com/media/a596ad9c9259912fb018ae0c3850a3ca/href</a></iframe><p>Adding an extra layer is also preferable for buildspec and even deployment files (AWS CloudFormation templates or Terraform files). You can add a check to ensure that no critical resource is being modified in those templates. The check would fail if</p><ul><li>a critical resource is being deleted.</li><li>suspicious permissions are being granted to an IAM user.</li><li>a critical resource is being modified.</li></ul><p>and many more depending on your use-case. To do so for AWS CloudFormation templates, you can create a stack policy like the one below which disables all updates to the production database.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%2528171%252C%2B184%252C%2B195%252C%2B1%2529%26t%3Dmaterial%26wt%3Dnone%26l%3Dyaml%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3D%25257B%25250A%252520%252520%252522Statement%252522%252520%25253A%252520%25255B%25250A%252520%252520%252520%252520%25257B%25250A%252520%252520%252520%252520%252520%252520%252522Effect%252522%252520%25253A%252520%252522Allow%252522%25252C%25250A%252520%252520%252520%252520%252520%252520%252522Action%252522%252520%25253A%252520%252522Update%25253A%252A%252522%25252C%25250A%252520%252520%252520%252520%252520%252520%252522Principal%252522%25253A%252520%252522%252A%252522%25252C%25250A%252520%252520%252520%252520%252520%252520%252522Resource%252522%252520%25253A%252520%252522%252A%252522%25250A%252520%252520%252520%252520%25257D%25252C%25250A%252520%252520%252520%252520%25257B%25250A%252520%252520%252520%252520%252520%252520%252522Effect%252522%252520%25253A%252520%252522Deny%252522%25252C%25250A%252520%252520%252520%252520%252520%252520%252522Action%252522%252520%25253A%252520%252522Update%25253A%252A%252522%25252C%25250A%252520%252520%252520%252520%252520%252520%252522Principal%252522%25253A%252520%252522%252A%252522%25252C%25250A%252520%252520%252520%252520%252520%252520%252522Resource%252522%252520%25253A%252520%252522LogicalResourceId%25252FProductionDatabase%252522%25250A%252520%252520%252520%252520%25257D%25250A%252520%252520%25255D%25250A%25257D&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%252528171%25252C%2B184%25252C%2B195%25252C%2B1%252529%26t%3Dmaterial%26wt%3Dnone%26l%3Dyaml%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3D%2525257B%2525250A%25252520%25252520%25252522Statement%25252522%25252520%2525253A%25252520%2525255B%2525250A%25252520%25252520%25252520%25252520%2525257B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252522Effect%25252522%25252520%2525253A%25252520%25252522Allow%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252522Action%25252522%25252520%2525253A%25252520%25252522Update%2525253A%25252A%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252522Principal%25252522%2525253A%25252520%25252522%25252A%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252522Resource%25252522%25252520%2525253A%25252520%25252522%25252A%25252522%2525250A%25252520%25252520%25252520%25252520%2525257D%2525252C%2525250A%25252520%25252520%25252520%25252520%2525257B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252522Effect%25252522%25252520%2525253A%25252520%25252522Deny%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252522Action%25252522%25252520%2525253A%25252520%25252522Update%2525253A%25252A%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252522Principal%25252522%2525253A%25252520%25252522%25252A%25252522%2525252C%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252522Resource%25252522%25252520%2525253A%25252520%25252522LogicalResourceId%2525252FProductionDatabase%25252522%2525250A%25252520%25252520%25252520%25252520%2525257D%2525250A%25252520%25252520%2525255D%2525250A%2525257D&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/00b938d273ac613c893350054f76b226/href">https://medium.com/media/00b938d273ac613c893350054f76b226/href</a></iframe><h4>Restrict build triggers</h4><p>Restrict the workflow to be run only on certain trigger events. For example, if you have a workflow that deploys your stack, configure it to run on pull request merge events and not all pull request events. Identify when and how frequently the workflows need to run and specify the trigger events accordingly. For example, if you are maintaining a Python library, you must be having a workflow that deploys the library to pip. In such cases, you can configure the trigger as creating a new release or a tag instead of a commit push.</p><p>While some workflows are required to be run on commit or pull request events, some may be user actions specific. For those, the workflows should be restricted to certain users. In my <a href="https://instigence.com/override-githubs-merge-strategies-cea7eb789e23">last article</a>, I created a GitHub Actions workflow to merge pull requests. The trigger event was a user commenting /merge on a pull request. For such events, there should be an additional check that ensures that the comment is coming only from designated users. This ensures that no one accidentally triggers a workflow that they should not be allowed to.</p><p>Restricting workflow start events at a branch level is also a good practice. You can configure the workflows to be run only if the event was triggered on a specific branch. For example, you may have a deployment workflow that is run on pull request merges. You can restrict it such that the workflow is run only if the base branch was of your deployable branches.</p><p>With GitHub Actions, you can take advantage of the if tag to restrict access like</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%2528171%252C%2B184%252C%2B195%252C%2B1%2529%26t%3Dmaterial%26wt%3Dnone%26l%3Dyaml%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Don%25253A%252520%25255B%252520push%252520%25255D%25250A%25250Ajobs%25253A%25250A%252520%252520buildAndTest%25253A%25250A%252520%252520%252520%252520runs-on%25253A%252520ubuntu-latest%25250A%25250A%252520%252520%252520%252520steps%25253A%25250A%252520%252520%252520%252520%252520%252520-%252520...%25250A%25250A%252520%252520deploy%25253A%25250A%252520%252520%252520%252520if%25253A%252520%252524%25257B%25257B%252520github.event_name%252520%25253D%25253D%252520%2527push%2527%252520%252526%252526%252520%2528github.ref%252520%25253D%25253D%252520%2527refs%25252Fheads%25252Fdev%2527%252520%25257C%25257C%252520github.ref%252520%25253D%25253D%252520%2527refs%25252Fheads%25252Fstaging%2527%2529%252520%25257D%25257D%25250A%252520%252520%252520%252520runs-on%25253A%252520ubuntu-latest%25250A%252520%252520%252520%252520needs%25253A%252520buildAndTest%25250A%25250A%252520%252520%252520%252520steps%25253A%25250A%252520%252520%252520%252520%252520%252520-%252520...&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%252528171%25252C%2B184%25252C%2B195%25252C%2B1%252529%26t%3Dmaterial%26wt%3Dnone%26l%3Dyaml%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Don%2525253A%25252520%2525255B%25252520push%25252520%2525255D%2525250A%2525250Ajobs%2525253A%2525250A%25252520%25252520buildAndTest%2525253A%2525250A%25252520%25252520%25252520%25252520runs-on%2525253A%25252520ubuntu-latest%2525250A%2525250A%25252520%25252520%25252520%25252520steps%2525253A%2525250A%25252520%25252520%25252520%25252520%25252520%25252520-%25252520...%2525250A%2525250A%25252520%25252520deploy%2525253A%2525250A%25252520%25252520%25252520%25252520if%2525253A%25252520%25252524%2525257B%2525257B%25252520github.event_name%25252520%2525253D%2525253D%25252520%252527push%252527%25252520%25252526%25252526%25252520%252528github.ref%25252520%2525253D%2525253D%25252520%252527refs%2525252Fheads%2525252Fdev%252527%25252520%2525257C%2525257C%25252520github.ref%25252520%2525253D%2525253D%25252520%252527refs%2525252Fheads%2525252Fstaging%252527%252529%25252520%2525257D%2525257D%2525250A%25252520%25252520%25252520%25252520runs-on%2525253A%25252520ubuntu-latest%2525250A%25252520%25252520%25252520%25252520needs%2525253A%25252520buildAndTest%2525250A%2525250A%25252520%25252520%25252520%25252520steps%2525253A%2525250A%25252520%25252520%25252520%25252520%25252520%25252520-%25252520...&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/89a21778548ed46a05793ec57244293e/href">https://medium.com/media/89a21778548ed46a05793ec57244293e/href</a></iframe><p>Such fine control is not possible with AWS CodeBuild, to bypass, you can use bash scripts like</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%2528171%252C%2B184%252C%2B195%252C%2B1%2529%26t%3Dmaterial%26wt%3Dnone%26l%3Dyaml%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dversion%25253A%2525200.2%25250A%25250Aphases%25253A%25250A%252520%252520build%25253A%25250A%252520%252520%252520%252520commands%25253A%25250A%252520%252520%252520%252520%252520%252520-%252520bash%252520conditional.sh&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%252528171%25252C%2B184%25252C%2B195%25252C%2B1%252529%26t%3Dmaterial%26wt%3Dnone%26l%3Dyaml%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dversion%2525253A%252525200.2%2525250A%2525250Aphases%2525253A%2525250A%25252520%25252520build%2525253A%2525250A%25252520%25252520%25252520%25252520commands%2525253A%2525250A%25252520%25252520%25252520%25252520%25252520%25252520-%25252520bash%25252520conditional.sh&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/cbd270e45be5aa349a3eb9262f3a0126/href">https://medium.com/media/cbd270e45be5aa349a3eb9262f3a0126/href</a></iframe><p>where the conditional.sh is like</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%2528171%252C%2B184%252C%2B195%252C%2B1%2529%26t%3Dmaterial%26wt%3Dnone%26l%3Dapplication%252Fx-sh%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3D%252523%2521%25252Fusr%25252Fbin%25252Fenv%252520bash%25250Aset%252520-e%25250A%25250AALLOWED_BRANCHES%25253D%252522dev%252520staging%252522%25250ACURRENT_BRANCH%25253D%252522main%252522%25250A%25250Adeploy%2528%2529%252520%25257B%252520%2528%25250A%252520%252520%252520%252520%252523%252520do%252520something%25250A%2529%25253B%252520%25257D%25250A%25250A%25255B%25255B%252520%252524%25257BALLOWED_BRANCHES%25257D%252520%25253D%257E%252520%2528%25255E%25257C%25255B%25255B%25253Aspace%25253A%25255D%25255D%2529%252524CURRENT_BRANCH%2528%252524%25257C%25255B%25255B%25253Aspace%25253A%25255D%25255D%2529%252520%25255D%25255D%252520%252526%252526%252520deploy%252520%25257C%25257C%252520echo%252520%252522Skip%252520deploy%252520as%252520%252524CURRENT_BRANCH%252520not%252520in%252520%252524ALLOWED_BRANCHES%252522&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%252528171%25252C%2B184%25252C%2B195%25252C%2B1%252529%26t%3Dmaterial%26wt%3Dnone%26l%3Dapplication%25252Fx-sh%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3D%25252523%252521%2525252Fusr%2525252Fbin%2525252Fenv%25252520bash%2525250Aset%25252520-e%2525250A%2525250AALLOWED_BRANCHES%2525253D%25252522dev%25252520staging%25252522%2525250ACURRENT_BRANCH%2525253D%25252522main%25252522%2525250A%2525250Adeploy%252528%252529%25252520%2525257B%25252520%252528%2525250A%25252520%25252520%25252520%25252520%25252523%25252520do%25252520something%2525250A%252529%2525253B%25252520%2525257D%2525250A%2525250A%2525255B%2525255B%25252520%25252524%2525257BALLOWED_BRANCHES%2525257D%25252520%2525253D%25257E%25252520%252528%2525255E%2525257C%2525255B%2525255B%2525253Aspace%2525253A%2525255D%2525255D%252529%25252524CURRENT_BRANCH%252528%25252524%2525257C%2525255B%2525255B%2525253Aspace%2525253A%2525255D%2525255D%252529%25252520%2525255D%2525255D%25252520%25252526%25252526%25252520deploy%25252520%2525257C%2525257C%25252520echo%25252520%25252522Skip%25252520deploy%25252520as%25252520%25252524CURRENT_BRANCH%25252520not%25252520in%25252520%25252524ALLOWED_BRANCHES%25252522&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/40915c140c29bb002a4877d51c006815/href">https://medium.com/media/40915c140c29bb002a4877d51c006815/href</a></iframe><h4>Require manual approval</h4><p>For deployment related workflows, especially the ones targeting production, you would want to have an additional layer of validation. In AWS CodePipeline, you can add a <a href="https://docs.aws.amazon.com/codepipeline/latest/userguide/approvals-action-add.html">manual approval</a> before the stack is finally deployed and available to the public. This adds an additional layer of checks that goes through the people of higher authority ensuring that broken/unsatisfactory builds are not deployed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/487/1*W6YZvCPrlFTo-o54dBZIkQ.png" /><figcaption>Source: <a href="https://www.trek10.com/blog/enforcing-two-person-rule-aws-codepipeline">https://www.trek10.com/blog/enforcing-two-person-rule-aws-codepipeline</a></figcaption></figure><h4>Have environment-specific IAM roles/users</h4><p>It is common practice to use a single IAM role or user and grant it access to the necessary resources in all the environments. While it results in maintaining fewer IAM roles and their permissions, it exposes a flaw that the role can access resources out of its scope. It is advisable to have a role for each environment so that the role for the development environment cannot access or modify the resources of the production environment.</p><p>With GitHub Actions, the same can be achieved like</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%2528171%252C%2B184%252C%2B195%252C%2B1%2529%26t%3Dmaterial%26wt%3Dnone%26l%3Dyaml%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Djobs%25253A%25250A%252520%252520deploy%25253A%25250A%252520%252520%252520%252520runs-on%25253A%252520ubuntu-latest%25250A%25250A%252520%252520%252520%252520steps%25253A%25250A%252520%252520%252520%252520%252520%252520-%252520name%25253A%252520Configure%252520AWS%252520credentials%252520for%252520branch%252520dev%25250A%252520%252520%252520%252520%252520%252520%252520%252520uses%25253A%252520aws-actions%25252Fconfigure-aws-credentials%252540v1%25250A%252520%252520%252520%252520%252520%252520%252520%252520if%25253A%252520github.ref%252520%25253D%25253D%252520%2527refs%25252Fheads%25252Fdev%2527%25250A%252520%252520%252520%252520%252520%252520%252520%252520with%25253A%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520aws-access-key-id%25253A%252520%252524%25257B%25257B%252520secrets.DEV_AWS_ACCESS_KEY_ID%252520%25257D%25257D%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520aws-secret-access-key%25253A%252520%252524%25257B%25257B%252520secrets.DEV_AWS_SECRET_ACCESS_KEY%252520%25257D%25257D%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520aws-region%25253A%252520ap-southeast-1%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520role-to-assume%25253A%252520%252524%25257B%25257B%252520secrets.DEV_AWS_ROLE_TO_ASSUME%252520%25257D%25257D%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520role-duration-seconds%25253A%2525201800%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520role-session-name%25253A%252520GitHubActionWorkflow%25250A%25250A%252520%252520%252520%252520%252520%252520-%252520name%25253A%252520Configure%252520AWS%252520credentials%252520for%252520branch%252520staging%25250A%252520%252520%252520%252520%252520%252520%252520%252520uses%25253A%252520aws-actions%25252Fconfigure-aws-credentials%252540v1%25250A%252520%252520%252520%252520%252520%252520%252520%252520if%25253A%252520github.ref%252520%25253D%25253D%252520%2527refs%25252Fheads%25252Fstaging%2527%25250A%252520%252520%252520%252520%252520%252520%252520%252520with%25253A%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520aws-access-key-id%25253A%252520%252524%25257B%25257B%252520secrets.STAGING_AWS_ACCESS_KEY_ID%252520%25257D%25257D%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520aws-secret-access-key%25253A%252520%252524%25257B%25257B%252520secrets.STAGING_AWS_SECRET_ACCESS_KEY%252520%25257D%25257D%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520aws-region%25253A%252520ap-southeast-1%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520role-to-assume%25253A%252520%252524%25257B%25257B%252520secrets.STAGING_AWS_ROLE_TO_ASSUME%252520%25257D%25257D%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520role-duration-seconds%25253A%2525201800%25250A%252520%252520%252520%252520%252520%252520%252520%252520%252520%252520role-session-name%25253A%252520GitHubActionWorkflow&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%252528171%25252C%2B184%25252C%2B195%25252C%2B1%252529%26t%3Dmaterial%26wt%3Dnone%26l%3Dyaml%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D27px%26ph%3D30px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Djobs%2525253A%2525250A%25252520%25252520deploy%2525253A%2525250A%25252520%25252520%25252520%25252520runs-on%2525253A%25252520ubuntu-latest%2525250A%2525250A%25252520%25252520%25252520%25252520steps%2525253A%2525250A%25252520%25252520%25252520%25252520%25252520%25252520-%25252520name%2525253A%25252520Configure%25252520AWS%25252520credentials%25252520for%25252520branch%25252520dev%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520uses%2525253A%25252520aws-actions%2525252Fconfigure-aws-credentials%25252540v1%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520if%2525253A%25252520github.ref%25252520%2525253D%2525253D%25252520%252527refs%2525252Fheads%2525252Fdev%252527%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520with%2525253A%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520aws-access-key-id%2525253A%25252520%25252524%2525257B%2525257B%25252520secrets.DEV_AWS_ACCESS_KEY_ID%25252520%2525257D%2525257D%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520aws-secret-access-key%2525253A%25252520%25252524%2525257B%2525257B%25252520secrets.DEV_AWS_SECRET_ACCESS_KEY%25252520%2525257D%2525257D%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520aws-region%2525253A%25252520ap-southeast-1%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520role-to-assume%2525253A%25252520%25252524%2525257B%2525257B%25252520secrets.DEV_AWS_ROLE_TO_ASSUME%25252520%2525257D%2525257D%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520role-duration-seconds%2525253A%252525201800%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520role-session-name%2525253A%25252520GitHubActionWorkflow%2525250A%2525250A%25252520%25252520%25252520%25252520%25252520%25252520-%25252520name%2525253A%25252520Configure%25252520AWS%25252520credentials%25252520for%25252520branch%25252520staging%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520uses%2525253A%25252520aws-actions%2525252Fconfigure-aws-credentials%25252540v1%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520if%2525253A%25252520github.ref%25252520%2525253D%2525253D%25252520%252527refs%2525252Fheads%2525252Fstaging%252527%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520with%2525253A%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520aws-access-key-id%2525253A%25252520%25252524%2525257B%2525257B%25252520secrets.STAGING_AWS_ACCESS_KEY_ID%25252520%2525257D%2525257D%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520aws-secret-access-key%2525253A%25252520%25252524%2525257B%2525257B%25252520secrets.STAGING_AWS_SECRET_ACCESS_KEY%25252520%2525257D%2525257D%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520aws-region%2525253A%25252520ap-southeast-1%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520role-to-assume%2525253A%25252520%25252524%2525257B%2525257B%25252520secrets.STAGING_AWS_ROLE_TO_ASSUME%25252520%2525257D%2525257D%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520role-duration-seconds%2525253A%252525201800%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520%25252520role-session-name%2525253A%25252520GitHubActionWorkflow&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/cf2686c2a0590b32f1b963af0e9ec39b/href">https://medium.com/media/cf2686c2a0590b32f1b963af0e9ec39b/href</a></iframe><p>We hope the above steps helped you in strengthening the security of your CI/CD pipeline. If you have any other tip that can help in securing the pipeline even more, please share in the comments so that the others can also benefit from it.</p><blockquote><strong>Have suggestions that can improve the process? Come join us! Juvoxa is hiring. Send an email to </strong><a href="mailto:hr@juvoxa.com"><strong>hr@juvoxa.com</strong></a><strong>.</strong></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=600a90ae6985" width="1" height="1" alt=""><hr><p><a href="https://medium.com/instigence/minimise-the-risk-of-misuse-in-ci-cd-pipeline-600a90ae6985">Minimise the risk of misuse in CI/CD pipeline</a> was originally published in <a href="https://medium.com/instigence">instigence</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Override GitHub’s merge strategies]]></title>
            <link>https://medium.com/@_samkitjain/override-githubs-merge-strategies-cea7eb789e23?source=rss-5523732f685------2</link>
            <guid isPermaLink="false">https://medium.com/p/cea7eb789e23</guid>
            <category><![CDATA[juvoxa]]></category>
            <category><![CDATA[git-merge]]></category>
            <category><![CDATA[github]]></category>
            <category><![CDATA[github-actions]]></category>
            <category><![CDATA[git]]></category>
            <dc:creator><![CDATA[Samkit Jain]]></dc:creator>
            <pubDate>Wed, 20 Oct 2021 04:13:54 GMT</pubDate>
            <atom:updated>2023-07-26T09:33:29.643Z</atom:updated>
            <content:encoded><![CDATA[<h3>Perform fast-forward merge on GitHub</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*GLsCm-hqHCHqWfdF" /><figcaption>Photo by <a href="https://unsplash.com/@yancymin?utm_source=medium&amp;utm_medium=referral">Yancy Min</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>In this article, you will learn how to override GitHub’s merge options using GitHub Actions. While the article focuses primarily on GitHub Actions, you can simulate the same on services like AWS CodeBuild as well.</p><p>My usual Git branch flow is</p><pre>&lt;feature&gt; -&gt; development -&gt; staging -&gt; production</pre><p>I would checkout a new&lt;feature&gt; branch from the development branch. Work on it and then raise a PR to merge it into the development branch. The changes would then be merged from the development to the staging branch and then from the staging to the production branch.</p><p>For merging a pull request, GitHub provides 3 options:</p><ol><li>Create a merge commit</li><li>Squash and merge</li><li>Rebase and merge</li></ol><h4>Create a merge commit</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/804/1*1uvT0mi0nGZT_mXMgoLJTA.png" /><figcaption>Even though the file changes are exactly the same in all 4 branches, except <strong>feature</strong>, all branches have additional merge commits.</figcaption></figure><p>This option adds all the commits from the feature branch to the base branch in a merge commit. The pull request is merged using the --no-ff option. This prevents git merge from doing a fast-forward and creates an additional merge commit.</p><p>GitHub does not allow configuring the fast-forward option and the option will always construct a new merge commit.</p><p><strong>Disadvantage:</strong> The history doesn&#39;t remain linear and there are unwanted extra commits.</p><h4>Squash and merge</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/790/1*Z9g7Invbga4EdUEuDjrWNA.png" /><figcaption>2 commits in the <strong>feature</strong> branch squashed into 1 and then the same process followed for others resulting in all 4 branches having the same code but different heads.</figcaption></figure><p>This option combines all the commits in the pull request into a single commit into the base branch. Instead of seeing all of the contributor’s individual commits from the feature branch, the commits are combined into one commit and merged into the base branch. The squashed commits are merged using the fast-forward option.</p><p><strong>Disadvantage:</strong> The information on the specific changes that were made and who made them is lost.</p><h4>Rebase and merge</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/798/1*aGW73Pbrukn_1Zphnv97dQ.png" /><figcaption>All 4 branches have the same commits but different commit SHAs for the merged commits.</figcaption></figure><p>This option rebases and adds all the commits from the feature branch onto the base branch individually without a merge commit. The rebased commits are merged using the fast-forward option.</p><p>The rebase and merge on GitHub will always update the committer information and create new commit SHAs, whereas git rebase outside of GitHub does not change the committer information when the rebase happens on top of an ancestor commit.</p><p><strong>Disadvantage:</strong> The commit SHAs are changed and even though all the branches have the same code, they are considered not in sync as the heads are different.</p><p>What I needed was a merge but with the fast-forward option. It would allow me to maintain a linear history without any unwanted commits. I prefer a linear commit history because it is easier to follow, the commit SHAs are the same across branches and it is easier to backtrack, revert and cherry-pick commits.</p><p>One option to achieve this is that you can open a terminal and run</p><pre>$ git checkout feature &amp;&amp; git pull origin feature<br>$ git checkout base &amp;&amp; git pull origin base<br>$ git merge feature<br>$ git push origin base</pre><p>but it requires that the developer who has push access to the branch must also be having access to a computer for the merge to happen. We also lose out on the information on what specific commands were run and when, in case we need to track that. The other alternative, which I implemented, was to use GitHub Actions to override the merge options provided by GitHub.</p><p>The GitHub Action is triggered when a user comments /merge on a pull request. It includes validation checks like</p><ol><li>The comment was on an open pull request.</li><li>The comment was by people with access.</li><li>The status checks on the pull request have passed.</li><li>There are no merge conflicts.</li><li>The comment exactly matches /merge.</li></ol><p>After the validation checks, the feature branch is merged into the base branch using the git rebase command and a success comment is added to the pull request. Here’s how the branch networks look like now:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/802/1*1T742RtM1gFXrsk_D4GYFg.png" /><figcaption>Everything is in sync.</figcaption></figure><p>You can find the GitHub Actions workflow file below and can see the workflow in action on a pull request <a href="https://github.com/samkit-jain/override-github-merge/pull/2">here</a>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0bc966508b85a198145c4799d1b9e84c/href">https://medium.com/media/0bc966508b85a198145c4799d1b9e84c/href</a></iframe><p>Repository for quick access:</p><p><a href="https://github.com/samkit-jain/override-github-merge">GitHub - samkit-jain/override-github-merge</a></p><p>This GitHub Actions workflow has helped me a lot in ensuring that the branches remain in sync and things are doable just from the GitHub.com interface. What you saw in this article is a simplified version of the action that we use. Per your needs, you can extend it to support more merge strategies, force pushes, auto-merge capability and so on.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cea7eb789e23" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Plan better sprints]]></title>
            <link>https://medium.com/instigence/plan-better-sprints-f08ead286a1e?source=rss-5523732f685------2</link>
            <guid isPermaLink="false">https://medium.com/p/f08ead286a1e</guid>
            <category><![CDATA[rfc]]></category>
            <category><![CDATA[juvoxa]]></category>
            <category><![CDATA[planning]]></category>
            <category><![CDATA[sprint]]></category>
            <category><![CDATA[sprint-planning]]></category>
            <dc:creator><![CDATA[Samkit Jain]]></dc:creator>
            <pubDate>Tue, 21 Sep 2021 13:44:48 GMT</pubDate>
            <atom:updated>2021-09-21T13:46:52.728Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*bHU-okFIba0aJSI3" /><figcaption>Photo by <a href="https://unsplash.com/@airfocus?utm_source=medium&amp;utm_medium=referral">airfocus</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>One of the most common traits you will find in successful companies is regular product updates. A trait that requires the discipline of proper planning. In this article, we will be covering how we improved sprint planning and meet targets at Juvoxa by introducing RFCs in the process.</p><h4>What is an RFC?</h4><p>An RFC, short for Request for Comment, is a document designed to help team members propose a solution to a problem and get feedback on that proposal. An RFC is used to drive clarity and consensus.</p><h4>Problems</h4><p>Not having an RFC in the planning process results in the developers thinking of a solution in their mind, giving a rough estimate and start coding. More often than not, during the implementation, unknowns start to uncover and they result in the deployment getting pushed. Estimations also take a hit because something that was expected to take X hours ends up taking &gt;&gt;X hours. Sometimes, even after the solution was implemented, the developers have to redo the work because of stakeholder expectation mismatch as the requirements were not properly understood. Since nothing is written, it also becomes difficult to refer to critical decision points.</p><h4>How does it help?</h4><p>Including an RFC in the process forces the developers to type the problem and the solution. When you write, you get more clarity and more edge cases start to unfold, handling which starts to improve the solution. Writing also improves the critical thinking of a person. Having a detailed RFC ensures that the authors have understood the problem well and the peer review process ensures that there is consensus amongst the team on the solution proposed.</p><p>This also indirectly results in proper task estimations as the developers would have greater clarity and understanding.</p><h4>How it fits in?</h4><p>At Juvoxa, we first create a PRD (Product Requirements Document) that gives a high-level overview of the problem and the expected user experience. Designs are then created for the same. After which, the backend and the frontend team create the RFCs. The RFCs then go through the review process and once they are approved, tickets/issues are created with estimations and actual coding starts. <em>(*tell us in the comments if you would want us to write an article on the full process as well)</em></p><h4>Template</h4><p>You can find the RFC template embedded below. If it does not load, click <a href="https://gist.github.com/samkit-jain/6880f34571f19f31bed6c92d86f28cbc">here</a>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c80c0028b2582a023b236c99d3ac30f9/href">https://medium.com/media/c80c0028b2582a023b236c99d3ac30f9/href</a></iframe><p>Feel free to use the template as needed. Do share in the comments how you adapted the template to your need and integrated it into your company’s processes.</p><p>Feel free to use the template as needed. Do share in the comments how you adapted the template to your need and integrated it into your company’s processes.</p><blockquote><strong>Think you can help us improve the planning process? Come join us! Juvoxa is hiring. Send an email to </strong><a href="mailto:hr@juvoxa.com"><strong>hr@juvoxa.com</strong></a><strong>.</strong></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f08ead286a1e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/instigence/plan-better-sprints-f08ead286a1e">Plan better sprints</a> was originally published in <a href="https://medium.com/instigence">instigence</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How we do testing at Juvoxa — Part 1]]></title>
            <link>https://medium.com/instigence/how-we-do-testing-at-juvoxa-part-1-7534ba11f7e?source=rss-5523732f685------2</link>
            <guid isPermaLink="false">https://medium.com/p/7534ba11f7e</guid>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[juvoxa]]></category>
            <dc:creator><![CDATA[Samkit Jain]]></dc:creator>
            <pubDate>Wed, 15 Sep 2021 13:31:55 GMT</pubDate>
            <atom:updated>2021-09-15T16:32:41.691Z</atom:updated>
            <content:encoded><![CDATA[<h3>How we do testing at Juvoxa — Part 1</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RHbmuBKmpjh1JBwV1-gmtQ.png" /><figcaption><a href="https://xkcd.com/1700/">https://xkcd.com/1700/</a></figcaption></figure><blockquote>Something that is untested is broken.</blockquote><p>This is part 1 of our multi-part series on how we do product testing at <a href="https://juvoxa.com">Juvoxa</a>. In this part, we will be discussing the API testing process.</p><p>This is how testing was done at Juvoxa</p><ol><li>Code</li><li>Deploy</li><li>Test</li><li>Bug? Go to step 1</li></ol><p>Sounds like something you also do? Test Driven Development (TDD) is usually sacrificed in favour of faster and regular deployments. This is a cost that many chose to bear and later regret once it goes out of control. Yes, it is understandable to make that tradeoff at the start as manpower, resources and time are limited but keeping it in check is more crucial. In this article, we’ll be sharing how we do API testing at Juvoxa and hope that our practices will help you recover the tradeoff cost.</p><blockquote>Our stack involves <strong>Python | Flask | Postgres</strong>. If your stack covers the same, read on.</blockquote><blockquote>Following is a bit theoretical and if you want to skip right to the fun coding part, skip a section below.</blockquote><h4>Manual vs Automated</h4><p>Wherever possible, stick with automating your testing process as much as possible. With manual, you rely on the developer or the tester to test out the functionality properly which is time-consuming and prone to bugs. Plus, you need to ensure that all the edge cases are covered. Automated testing with TDD saves the developer time with the process being fully <strong>automated</strong>.</p><ul><li><strong>No unintended consequences</strong> as bugs are caught earlier on.</li><li>A <strong>faster</strong> transition from the development phase to the QA phase.</li><li><strong>Reproducible user behaviour</strong> with integration testing.</li><li>An extra hour writing the tests today saves us an extra day tomorrow.</li><li>Overall more <strong>reliable and stable</strong> API system.</li><li>No more (okay, fewer) <a href="https://xkcd.com/1739">XKCD #1739</a> events.</li><li>Boosts developer confidence.</li></ul><h4>Minimal Requirements</h4><p>The testing setup should have the following at the least</p><ul><li>Cover both the positive (API endpoint returning HTTP 2XX response) and negative (API endpoint returning non-HTTP 2XX response) workflows.</li><li>Validate not just the HTTP status code, but also the response payload.</li><li>For a Pull Request (PR) to be deemed ready for review, it must have a test that passes with the code changes made in the PR but fails without them. The code coverage should improve or at the least remain the same. But should never decrease.</li><li>In the CI/CD pipeline, all the tests must pass before the changes can be deployed.</li></ul><h4>API Testing Strategies</h4><p><strong>Unit Testing<br></strong>This involves testing a single API endpoint. The tests for that single endpoint run various simulations including both positive and negative workflows and validate that the endpoint is working as expected or not. For example, testing the login API endpoint with the correct credentials.</p><p><strong>Integration testing<br></strong>This involves running multiple API endpoints in a single test. This is useful for simulating user scenarios and testing API endpoints that are dependent on each other. For example, simulating the flow in which a doctor is creating a program on the platform which involves multiple endpoints to create a base program, add contents, add tags, etc.</p><h3>Coding Time</h3><p><em>* Assuming you already have a Flask app created and use Docker.</em></p><p>Before the tests can be run, you need to create an ephemeral database that lives for as long as your tests are run. You don’t want to be running the tests on the production database.</p><p>To start a sample PostgreSQL database, you can use the <a href="https://hub.docker.com/_/postgres">official Docker image</a> like so (if you are using a different database backend then you can use a different Docker image)</p><pre>$ docker run -d — rm -P -p 127.0.0.1:5432:5432 -e POSTGRES_DB=db_name -e POSTGRES_USER=username -e POSTGRES_PASSWORD=password — name postgres-container postgres:13.1</pre><p>The above will start a PostgreSQL 13.1 server and keep it running in the background and also create an empty database by the name db_name that can be accessible by the Postgres user username with the password password. The database can be accessed over localhost at port 5432. Equivalent connection string postgresql://username:password@localhost:5432/db_name.</p><p>Setup your testing directory as</p><pre>tests/<br>|-- __init__.py<br>|-- api<br>|   |-- test_00_signup_api.py<br>|   `-- test_01_signin_api.py<br>`-- conftest.py</pre><p>Tests are run in alphabetical order, so if you want your tests to be run in a certain order, prefix them with 00, 01, 02, …<br>conftest.py — Configuration file where you can register hooks.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6fd808bcb449c580040e7cd3e19e786b/href">https://medium.com/media/6fd808bcb449c580040e7cd3e19e786b/href</a></iframe><p>It is better to organise your test files in the api/ folder and have a separate file for each API.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/52efc7f0b67cfc6c6cca0c60a79f985c/href">https://medium.com/media/52efc7f0b67cfc6c6cca0c60a79f985c/href</a></iframe><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4b2950f06acdee550b6d5d4d32cbc3da/href">https://medium.com/media/4b2950f06acdee550b6d5d4d32cbc3da/href</a></iframe><p>Notice that even in the individual testing files, using the method name with 00, 01 as a suffix to ensure that the tests are run always in the required order. You can leverage the testing order to also populate the same database on the go and don’t need to rely on having a prepopulated database (though that is a choice as well).</p><p>To run the tests, first, install <a href="https://docs.pytest.org/en/6.2.x/">pytest</a></p><pre>$ python -m pip install pytest  # Required only once<br>$ python -m pytest tests/</pre><p>If everything is ok, you should be seeing a green tick saying all tests have passed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lOjhgo_dH7RhycK1iQcZZg.png" /></figure><p>If you want to run only a subset of tests, you can use the -k option as provided by pytest. More details can be found <a href="https://docs.pytest.org/en/6.2.x/usage.html">here</a>.</p><p>The above was a very simple example that will help you get started with integrating testing in your API development process. You can extend the test cases above to suit your needs. The above example demonstrated unit testing the APIs. To create scenarios and perform integration testing, you can update the tests to call multiple APIs in sequence and validate each (for example, simulating a flow where a user registers but does not verify the email and then tries to login and perform restricted actions).</p><h4>CI/CD</h4><p>We use <a href="https://aws.amazon.com/codebuild/">AWS CodeBuild</a> for our CI/CD flow. A sample buildspec.yml file to run the tests as part of your CI pipeline will look like</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5b93dbd939a7066d83f30cf7d8675d34/href">https://medium.com/media/5b93dbd939a7066d83f30cf7d8675d34/href</a></iframe><h4>Code Coverage</h4><p>Code coverage is a means to measure how much of your code is executed when running the tests. The higher the coverage, the better. There are multiple coverage report generators available that go along with pytest with the most popular ones being <a href="https://pytest-cov.readthedocs.io/en/latest/">pytest-cov</a> and <a href="https://coverage.readthedocs.io/en/coverage-5.5/">Coverage.py</a>.</p><p>We used pytest-cov and to use it, you just need to install it via pip and pass the required parameters when running the tests.</p><pre>$ python -m pip install pytest-cov<br>$ python -m pytest --cov=./ --cov-report=xml tests/</pre><p>To visualise the coverage in a better way and include it in your CI setup you need to use a service like <a href="https://about.codecov.io/">Codecov</a>. Create an account and authenticate GitHub. Once done, you just need to upload the coverage.xml report generated above to Codecov and let it handle the rest. For private repos, you can do so using</p><pre>$ bash codecov_upload.sh</pre><p>where codecov_upload.sh has the content as</p><pre>#!/bin/bash<br>bash &lt;(curl -s <a href="https://codecov.io/bash">https://codecov.io/bash</a>) -t YOUR-CODECOV-TOKEN</pre><p>The buildspec.yml with the Codecov will now look</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/913b76dcc69a414d8b7b678ea29af7f9/href">https://medium.com/media/913b76dcc69a414d8b7b678ea29af7f9/href</a></iframe><p>Tests should not be considered as a burden but rather as a means to empower the team to build better systems. They give you confidence when adding new features and allow you to scale your development with confidence as the codebase grows.</p><p>In the upcoming articles, we will be discussing how we test our frontend. Follow us to not miss out on any new articles.</p><blockquote><strong>Think you can help us improve the testing process? Come join us! Juvoxa is hiring. Send an email to </strong><a href="mailto:hr@juvoxa.com"><strong>hr@juvoxa.com</strong></a><strong>.</strong></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7534ba11f7e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/instigence/how-we-do-testing-at-juvoxa-part-1-7534ba11f7e">How we do testing at Juvoxa — Part 1</a> was originally published in <a href="https://medium.com/instigence">instigence</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Physio Pose: A virtual physiotherapy assistant]]></title>
            <link>https://medium.com/@_samkitjain/physio-pose-a-virtual-physiotherapy-assistant-7d1c17db3159?source=rss-5523732f685------2</link>
            <guid isPermaLink="false">https://medium.com/p/7d1c17db3159</guid>
            <category><![CDATA[physiotherapy]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[computer-vision]]></category>
            <category><![CDATA[pose-estimation]]></category>
            <dc:creator><![CDATA[Samkit Jain]]></dc:creator>
            <pubDate>Wed, 04 Mar 2020 07:24:01 GMT</pubDate>
            <atom:updated>2020-03-13T08:17:09.112Z</atom:updated>
            <content:encoded><![CDATA[<h4>A Proof-Of-Concept for a real-time personal virtual trainer for physiotherapy exercises.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*H2ViR54BACV0patPZmhHnw.gif" /><figcaption>Pose estimation on a woman doing jumping jacks</figcaption></figure><p><strong>Background<br></strong>Physiotherapy treatments are usually long-running as it takes time to restore a person’s movement. One has to perform the same set of exercises for a few months every day with the correct posture to regain the movement. Visiting a physiotherapist for every session can be pretty expensive and not everyone can afford that. Those who do the exercise at the comforts of their home have to make sure to get the posture and movement right.</p><p>This project is an attempt to create a system using computer vision that can guide, provide instant feedback and act as a personal virtual trainer that would help people do exercises. A system that focuses more on form than on reps.</p><p><strong>GitHub repo is at </strong><a href="https://github.com/samkit-jain/physio-pose"><strong>https://github.com/samkit-jain/physio-pose</strong></a><strong>.</strong> (It starts off from when I decided to go ahead with <em>openpifpaf</em>)</p><h4>Usage</h4><p>The main script to run is physio.py. A sample run command could look like:</p><pre>python physio.py --exercise seated_right_knee_extension --joints --skeleton --save-output</pre><p>There are more options available and the full list can be seen by running python physio.py -h.</p><p>The result would look something like the GIF below where the top portion would show instructions to the user and the bottom would be the skeleton of the user.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*98QNs6F1wlM_3G7vAOYoNQ.gif" /><figcaption>Seated knee flexion and extension</figcaption></figure><h4><strong>How it works</strong></h4><p>Let’s start with pose estimation. One resource limitation that I faced was that my laptop does not have an Nvidia GPU which means no CUDA and had to use a pose estimation model that could work on a CPU. For pose estimation, I experimented with the following projects that could work on CPU only machines:</p><ol><li><strong>LightTrack:</strong> Implementation of “LightTrack: A Generic Framework for Online Top-Down Human Pose Tracking”. <a href="https://github.com/Guanghan/lighttrack">GitHub</a>.</li><li><strong>Lightweight OpenPose:</strong> Implementation of “Real-time 2D Multi-Person Pose Estimation on CPU: Lightweight OpenPose”. <a href="https://github.com/Daniil-Osokin/lightweight-human-pose-estimation.pytorch">GitHub</a>.</li><li><strong>Openpifpaf:</strong> Implementation of “PifPaf: Composite Fields for Human Pose Estimation” in PyTorch. <a href="https://github.com/vita-epfl/openpifpaf/">GitHub</a>.</li><li><strong>Tf-pose-estimation:</strong> TensorFlow implementation of OpenPose. <a href="https://github.com/ildoonet/tf-pose-estimation">GitHub</a>.</li></ol><p>Out of these, I found <em>openpifpaf</em> and <em>tf-pose-estimation</em> to have the highest FPS (frames per second) rates. Of the two, <em>openpifpaf</em> gave more accurate results in cases when the full body wasn’t visible or the body was lying sideways.</p><p>The next step involves assessing the movement of the joints. We do that by first checking whether all the required joints have been identified and are visible in the frame. Then, the user is instructed to get in the starting position. Next, the user is instructed to perform the subsequent steps involved in the exercise. The feedback is provided based on the angle made by the keypoints being covered.</p><p>Let’s take the example of seated knee flexion and extension with the right leg. It is best done sitting in a chair. The sub-steps can be written as</p><ol><li>The right leg should be in a seated position, i.e., making an inner angle in the range of 120 to 150 degrees.</li><li>Extend the right leg such that the inner angle is 180 degrees.</li><li>Bring the right leg back to the starting position.</li></ol><p>The program follows the same instructions and guides the user. First checks whether the keypoints are visible. Then waits for the user to move the right leg in the seated position. Once it is, waits for the user to extend and straighten the leg. Once it is, waits for the user to bring the leg back to the starting position. During each of the waiting periods, it provides valuable feedback to the user. For example, if the leg is not in starting position, instructs to do so.</p><h4><strong>3D</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*LvBo7369uVH2aqvuqHKLAA.gif" /><figcaption>3D bent elbow shoulder rolls</figcaption></figure><p>3D data feed provides more real to life impression of a human body and can help in providing much more accurate results.</p><p>This experiment was short-lived as creating a 3D image from a 2D image is still not near perfect as can be seen in the GIF to the left. The 2D video which was provided had the person doing a seated bent elbow shoulder rolls. The information is clearly lost in the generated 3D.</p><h3>Pose similarity</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*DmnFt9mnqHNBW3JoP8Jc2w.gif" /><figcaption><a href="https://youtu.be/TrHdgq7rFsc">https://youtu.be/TrHdgq7rFsc</a></figcaption></figure><p>The current application focuses on providing real-time assessment to the exercise that the person is doing. A good feature could be to provide a score on how close the motion was to the perfect motion reference. The solution also needs to consider the variance in the duration of the videos as it is not guaranteed that both the videos would be of the same length and performing the same motion at the same point in time at the same speed.</p><p>Code for this is at <a href="https://github.com/samkit-jain/physio-pose/blob/master/pose_compare.py"><strong>pose_compare.py</strong></a>.</p><h4><strong>Run</strong></h4><p>You need 2 videos. Run pose estimation using physio.py on both the videos and save their CSV results. The execution would be similar to:</p><pre>$ python physio.py --video video1.mp4 --csv-path video1.csv<br>$ python physio.py --video video2.mp4 --csv-path video2.csv</pre><p>To calculate how similar the 2 poses are, run:</p><pre>$ python pose_compare.py video1.csv video2.csv</pre><p>This would output a decimal number. The lower the better.</p><h4>Explanation</h4><p>The implementation is inspired by the research paper <a href="https://arxiv.org/abs/1906.12171">Schneider P., Memmesheimer R., Kramer I., Paulus D. (2019) Gesture Recognition in RGB Videos Using Human Body Keypoints and Dynamic Time Warping</a>. Even though the paper focuses on gesture recognition, we can leverage the processing techniques mentioned in the paper along with dynamic time warping to measure the similarity between sequences which may vary in speed.</p><p>It is a 4 step process:</p><ol><li><strong>Translation:</strong> All the key points are translated such that the nose key point becomes the origin of the coordinate system.</li><li><strong>Scaling:</strong> The key points are scaled such that the distance between the left shoulder and right shoulder key point becomes 1.</li><li><strong>Dimension Selection:</strong> Joints that do not move significantly in the sequence are removed.</li><li><strong>Dynamic Time Warping:</strong> An approximate Dynamic Time Warping algorithm that provides optimal or near-optimal alignments with an O(N) time and memory complexity.</li></ol><h3>Improvements</h3><p>There’s a lot of scope for improvement in this implementation and pull requests are welcome.</p><ol><li><strong>Stabilisation:</strong> As is evident from the demo GIF shared above, the key point identification can be stabilised to give a smooth transition effect.</li><li><strong>Multiple reps:</strong> Support for doing multiple repetitions of an exercise.</li><li><strong>Lightweight model:</strong> A model that is light enough to run predictions at 30 fps even on a CPU machine and yet accurate enough to be usable.</li><li><strong>Mobile application:</strong> A mobile representation of the application.</li></ol><p>I would like to extend a special thanks to <a href="https://www.linkedin.com/in/amitgupta28/">Mr. Amit Prakash Gupta</a> who gave me the opportunity to work on this project.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7d1c17db3159" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why OCR-ing a bank statement is a bad idea]]></title>
            <link>https://medium.com/zodhana/why-ocr-ing-a-bank-statement-is-a-bad-idea-6f976172b7a9?source=rss-5523732f685------2</link>
            <guid isPermaLink="false">https://medium.com/p/6f976172b7a9</guid>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[ocr]]></category>
            <dc:creator><![CDATA[Samkit Jain]]></dc:creator>
            <pubDate>Sun, 03 Jun 2018 04:08:06 GMT</pubDate>
            <atom:updated>2018-06-03T04:09:09.423Z</atom:updated>
            <content:encoded><![CDATA[<p>A lending company needs the answer to just one question when giving loans, “Will the borrower be able to repay the loan?”.</p><p>Coming to a binary (yes or no) answer to that question involves a lot of work (both manual and automated). Lending companies go through various financial documents of the borrower and perform a thorough analysis to come to a conclusion. With the advancements in artificial intelligence and machine learning, some companies have been able to reduce the time to approve a loan to a day (but don’t share the percentage of bad loans probably because OCR systems are still not as good as they want it to be). A bank account statement can be tens or hundreds of pages long with thousands of transactions. An individual’s account statement can contain just a few hundred transactions while a corporate’s can be in thousands. To understand the past spending behaviour of a borrower and predict the future loan repaying ability, one of the financial document that every lending company asks for is a bank account statement.</p><p>Bank statements shared come in all types imaginable. You’ll find PDFs (both bank generated and scanned copies), CSVs, images, in rare cases even HTMLs. In this post, we’ll talk about the most common type, image. Even in images, you’ll see a lot of variety,</p><ul><li>screenshots</li><li>blurry photos</li><li>high-resolution photos</li><li>low-resolution photos</li><li>photos with folded pages</li><li>photos in bad lighting</li></ul><p>Dealing with images has always been problematic. You cannot simply copy-paste the text and neither can you accurately get the text because of the ambiguous shape of letters and numbers. They are human readable but not machine readable.</p><h4>OCR to the rescue!</h4><p>Optical Character Recognition or OCR is a technology that recognizes text within an image. Humans have the ability to easily understand the text in an image, however complex (after all, we are the masters of the sacred texts!).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*wpKwk2JzvAcx0zrL." /><figcaption>Source: <a href="https://www.reddit.com/r/comics/comments/7xi2bh/the_sacred_texts_oc/">https://www.reddit.com/r/comics/comments/7xi2bh/the_sacred_texts_oc/</a></figcaption></figure><p>Over the past few years OCR solutions have really gotten much better. They are able to recognise handwritten texts with a good amount of accuracy. Giants like Google and Microsoft have also invested in the field and have come up with their own text recognition products.</p><p>It’s a known fact that OCR works well when the characters are printed, image quality is high and lighting is ideal. Bank statement images shared by the borrower have one thing going for them, they contain printed text. But this is not enough. Even with ideal conditions, it won’t be enough.</p><p>Majority of errors in OCR systems are because of incorrect classification. It usually misclassifies in cases where the features of a letter and number are same. Some of the ambiguous cases are,</p><ul><li>O (letter) and 0 (number)</li><li>I (uppercase i) and 1 (number)</li><li>l (lowercase L) and 1 (number)</li><li>S (uppercase s) and 5 (number)</li><li>Z (uppercase z) and 2(number)</li></ul><p>When using OCR for general purpose text detection, this ambiguity might not be a serious concern, but when using it on a bank statement especially for lending purposes, this is a serious concern. Misclassifying 50,000 as SO,OOO is quite serious. It would lead to missing important transactional entries.</p><p>Images shared by borrowers are usually not in the ideal condition. They are in bad lighting, blurry, low res, have pen/pencil markings, pages are folded, etc. All these factors act as a catalyst and lead to more and more incorrectly classified characters.</p><h4>Some examples where OCR didn’t work for us</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/727/1*VHcSfrxWRLPwf1RRD0PJXA.png" /><figcaption><strong>Could not detect all the text. Misread . as : </strong>(Microsoft Azure’s Computer Vision API)</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/648/1*4fGmsDqU47EuoCZAZA-BLA.png" /><figcaption><strong>Could not detect every text. Misread 0 as O and , as . </strong>(Google’s Cloud Vision API)</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/784/1*9bqPIbWaDnZ1OJQmzLorig.png" /><figcaption><strong>Could not detect any text </strong>(Microsoft Azure’s Computer Vision API)</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/648/1*04YC7FnRzs6ziwMpXZm2Mw.png" /><figcaption><strong>Misread , as . and C as G and . as : </strong>(Google’s Cloud Vision API)</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/572/1*jhVd3LQ8wfUV1fwWHaKCsQ.png" /><figcaption><strong>Misread Z as 2 </strong>(Google’s Cloud Vision API)</figcaption></figure><h4>Current state of OCR systems</h4><p>When Inkredo was into P2P lending, we too accepted images of bank account statements and manually typed every entry in an Excel sheet. Expectedly, this was a time taking process and we tried multiple OCR solutions — in-house, open-sourced and paid — but, none of them gave us the desired result. Some of them worked really well with bad quality photos but all of them struggled with ambiguous characters.</p><p>To conclude, OCR is not reliable for text detection in financial documents where reading a comma as a dot (or vice-versa) can make a significant difference. PDF (containing text and not scanned images) should be the preferred type because it’s not as easy to manipulate as a CSV and is easier to extract text from PDF-encapsulated files rather than images. Plus, you won’t have to buy expen$ive OCR solutions.</p><p>Do you think OCR is reliable when it comes to credit risk assessment? Share your thoughts in the comments.</p><blockquote>Click <a href="https://www.inkredo.in">here</a> to try a demo of Inkredo.</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6f976172b7a9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/zodhana/why-ocr-ing-a-bank-statement-is-a-bad-idea-6f976172b7a9">Why OCR-ing a bank statement is a bad idea</a> was originally published in <a href="https://medium.com/zodhana">Zodhana</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to deploy Go web application on AWS Elastic Beanstalk]]></title>
            <link>https://medium.com/@_samkitjain/how-to-deploy-go-web-application-on-aws-elastic-beanstalk-81e67c69dfa1?source=rss-5523732f685------2</link>
            <guid isPermaLink="false">https://medium.com/p/81e67c69dfa1</guid>
            <category><![CDATA[aws]]></category>
            <category><![CDATA[elastic-beanstalk]]></category>
            <category><![CDATA[deployment]]></category>
            <category><![CDATA[go]]></category>
            <dc:creator><![CDATA[Samkit Jain]]></dc:creator>
            <pubDate>Wed, 09 May 2018 05:02:38 GMT</pubDate>
            <atom:updated>2018-05-09T05:02:38.402Z</atom:updated>
            <content:encoded><![CDATA[<p>Deploying the version one of an app to production for the first time is never a walk in the park. We too faced obstacles when deploying the backend of 91paisa (written in Go) on Amazon Web Services (AWS) Elastic Beanstalk. After multiple trials and support from the AWS Support, we finally deployed the initial version of 91paisa. This article will help you if you too are figuring out how to deploy your Go web app on AWS Elastic Beanstalk.</p><p>You can deploy Go web applications on AWS Elastic Beanstalk in a couple of ways:</p><ol><li>Uploading the source code</li><li>Uploading the binary</li></ol><h3>Uploading the source code</h3><p>Create a Zip file which contains application.go (entry point of your application) file at the root along with the source code. Also, create three additional files - build.sh, Buildfile and Procfile.</p><p>The directory structure will look like:</p><pre>.<br>├── application.go<br>├── datastore<br>│   └── db.go<br>│   └── ...<br>├── api<br>│   └── api.go<br>│   └── ...<br>├── public<br>│   └── index.html<br>├── build.sh<br>├── Buildfile<br>├── Procfile<br>└── .ebextensions<br>    └── ...</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/69bd11abf7f7d79c326031ef3e07f549/href">https://medium.com/media/69bd11abf7f7d79c326031ef3e07f549/href</a></iframe><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6691bd483186d460161a10ef50337345/href">https://medium.com/media/6691bd483186d460161a10ef50337345/href</a></iframe><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e70b5ad56dfa7311e6e2e99ebb832b5f/href">https://medium.com/media/e70b5ad56dfa7311e6e2e99ebb832b5f/href</a></iframe><p>Now, upload the Zip file to Elastic Beanstalk and wait. If everything works, congratulations! If not, read on.</p><p><strong>We followed the same process but the environment health immediately degraded.</strong> Digging through the logs we found,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cdbWt0Zs59Gjqq81ZBVvng.png" /></figure><p>The build command was <strong>failing</strong>.</p><p>We ran eb ssh to SSH into the instance and could find the files downloaded at /var/app/current/ (GOPATH) meaning that the datastore folder was located at /var/app/current/datastore while the application was looking for it in /var/app/current/src/github.com/91paisa/backend/datastore.</p><p>The build command was failing because of the project layout. We follow the standard structure (and naming convention) where the source code is located at go/src/<strong>github.com/91paisa/backend</strong>/ which can be imported by specifying the complete path, i.e. import &quot;github.com/91paisa/backend/datastore&quot;. Since beanstalk does not know about the project layout it is not creating one and hence the build was failing.</p><p>You can get around this by either (a) using relative paths for your packages in the import statement or (b) dividing the application into packages such that you can install them using go get.</p><blockquote><em>Note:</em><strong><em> Elastic Beanstalk does not delete the previous files.</em></strong> Example, your source code initially contained just three files — application.go, api.go and cmd.go — you uploaded the Zip and everything works great. Now, you don’t need the cmd.go file and you delete it and re-upload the Zip. On your environment, the cmd.go file will still be present and you’ll have to manually remove it or restart the environment. <em>Whenever the load balancer creates a new instance it contains only the latest files though.</em></blockquote><h3>Uploading the binary</h3><p>Create binary using</p><pre><strong>GOARCH=amd64 GOOS=linux go build -o bin/application application.go</strong></pre><p>This will create a bin folder containing the binary. Now, create a Zip file containing the bin folder. You can also add additional files like assets and .ebextensions folder.</p><p>The directory structure of the Zip will look like</p><pre>.<br>├── bin<br>│   └── application<br>├── public<br>│   └── index.html<br>└── .ebextensions<br>    └── ...</pre><p>You can also create a shell script to automate the process. A basic shell script that fits my use case:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a0e4c4013c086b16c8a2453b91de62e0/href">https://medium.com/media/a0e4c4013c086b16c8a2453b91de62e0/href</a></iframe><p>Don’t forget to chmod +x create_zip.sh. Run it using ./create_zip.</p><h3>Concluding remarks</h3><p>We hope this article helped you in deploying your Go app to AWS Elastic Beanstalk. If you are looking to deploy on EC2 or DigitalOcean, the process becomes much more easier. All you have to do is build the executable binary and run it as a service on the server.</p><p>Know a better way to deploy? Please share your knowledge in the comments.</p><p><em>Wanna chat? Drop me a message on on </em><a href="https://twitter.com/_samkitjain"><em>Twitter</em></a><em> or connect with me on </em><a href="https://www.linkedin.com/in/samkit-jain/"><em>LinkedIn</em></a><em>!</em></p><blockquote><em>Originally published at </em><a href="http://www.91paisa.com/blog/how-to-deploy-go-web-application-on-aws-elastic-beanstalk">http://www.91paisa.com/blog/how-to-deploy-go-web-application-on-aws-elastic-beanstalk</a></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=81e67c69dfa1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Developing a bank statement analyser]]></title>
            <link>https://medium.com/@_samkitjain/developing-a-bank-statement-analyser-7470bffbe5e2?source=rss-5523732f685------2</link>
            <guid isPermaLink="false">https://medium.com/p/7470bffbe5e2</guid>
            <category><![CDATA[analysis]]></category>
            <category><![CDATA[fintech]]></category>
            <category><![CDATA[parser]]></category>
            <dc:creator><![CDATA[Samkit Jain]]></dc:creator>
            <pubDate>Thu, 14 Dec 2017 05:54:18 GMT</pubDate>
            <atom:updated>2019-03-18T10:55:32.353Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1LxrUIgnG-4OCXUfD55cRA.png" /><figcaption>Snapshot of sample output</figcaption></figure><h4>How we achieved accuracy of over 90% after reading 800+ transactions</h4><p>At <a href="http://www.inkredo.in">Inkredo</a>, we perform flow-based credit assessment to determine the monthly repaying capacity of a customer. Our customers are small &amp; underbanked retailers who are running a bootstrapped and consistently profitable business, yet they remain excluded from formal credit. Formal institutions have shied to lend to lower-middle income group because the cost-benefit analysis of lending and collections do not offset the cost of originating and recovery. There is no cost-effective measure to monitor income/solvency and ensure timely repayment.</p><p>The assessment involves calculating useful analysis from the bank statement of our customer. This task requires copy pasting every transaction from the PDF of bank statement (containing tens of pages with hundreds of rows) to an Excel file, cleaning the copied data, and then using Excel wizardry to perform some statistical operations. Imagine using Ctrl+C and Ctrl+V almost a thousand times every other day. As you might have guessed, this involves a lot of human interaction and typically takes us a day to complete a single bank statement.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*h0FDReC8WLCR4SIECGSyGA.png" /><figcaption><strong>When you just want to copy a row but it selects the whole column</strong></figcaption></figure><p>With our growing user base, a solution was required to reduce the effort and time required. A smart solution to generate insights within seconds with minimal human interaction.</p><h4>Input</h4><p>Bank statement in PDF</p><h4>Output</h4><ul><li>Sources of earning</li><li>Merchant transactions</li><li>Operational expenses</li><li>Recurring transactions</li><li>Debt (if any)</li><li>Default (if any)</li></ul><h3><strong>Process</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AOm1JAjSMdpc4cxG34XS5g.png" /><figcaption>The 5 step process</figcaption></figure><h3>The Research</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*01sOMYHjoZyZRsQkKV8v8Q.gif" /><figcaption><a href="https://media.giphy.com/media/l0HlKsNo3QNx4AHFC/giphy.gif">source</a></figcaption></figure><p>Before starting with the development, we tackled the problem manually. For each bank statement (from various banks), read all the transactions, highlighted keywords and assigned appropriate labels and categories to each. Then with the generated mapping created a set of keywords for each category with priorities assigned.</p><h3>The dataset</h3><p>A bank statement containing transactions from over six months of a person running a business is usually more than 20 pages long with around 1,000 transactions. Columns are generally of date, particular, balance, deposit, withdrawal, etc. For a specific bank, the result is pretty consistent and easy to play with, but every bank has its format for bank statements. Count of columns, positioning of columns, separators, text format and abbreviations vary.</p><p>The columns we require are</p><ul><li>Date of transaction</li><li>Particulars</li><li>Deposit amount</li><li>Withdrawal amount</li><li>Closing balance</li><li>Cheque/Reference number</li></ul><p>These columns are found in every bank statement. Example,</p><p><strong>HDFC</strong></p><p>Date | Narration | Chq./Ref.No. | Value Dt | Withdrawal Amt. | Deposit Amt. | Closing Balance</p><p><strong>IDBI</strong></p><p>Srl | Txn Date | Value Date | Description | Cheque No | CR/DR | CCY | Trxn Amount | Balance</p><p><strong>ICICI</strong></p><p>Tran Date | Value Date | Particulars | Location | Chq.No | Withdrawals | Deposits | Balance (INR)</p><p>The naming convention might be different, but the purpose of every column remains the same.</p><p>Created a dictionary called BANK_DETAILS that contains the position of the required column. Example,</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5c8462111d2f6f6bb0590bcd060eecce/href">https://medium.com/media/5c8462111d2f6f6bb0590bcd060eecce/href</a></iframe><h3>Reading the bank statement</h3><p>Reading tables from PDF documents is not an easy task. Even copying data from tables doesn’t work properly most of the time. Thankfully, there’s an open-source library available called <a href="https://github.com/tabulapdf/tabula/">tabula</a> that can extract tables from a PDF with almost accurate results. We used its Python wrapper <a href="https://github.com/chezou/tabula-py">tabula-py</a> for the data extraction.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/dbf342d4d39b0989781f0a27ad8e8551/href">https://medium.com/media/dbf342d4d39b0989781f0a27ad8e8551/href</a></iframe><h3>Making the extracted data consistent</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9aBgAIm9G9gt5kwLqYHiOA.png" /><figcaption>Table header from ICICI bank statement</figcaption></figure><p>Every page with transactions table of the ICICI bank statement consists of this header row. This row is useless for the system as we are only targeting transactions.</p><p><strong>Aim:</strong> Remove header rows from the list of transactions.</p><p><strong>Solution:</strong> From reading multiple transactions from numerous bank statements we realised that the closing balance column is always the last. So, a header can be considered as rows (why plural? see next task) starting from the first row till the row where closing balance is not null. Then, go through all the rows and if the row is a part of headers, remove it. In the end, we have rows without any header.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/32a3b31deb04a0cf5910fe58f6778fbb/href">https://medium.com/media/32a3b31deb04a0cf5910fe58f6778fbb/href</a></iframe><figure><img alt="" src="https://cdn-images-1.medium.com/max/955/1*8EECNBgQVqK1xQ6t_U1mGw.png" /><figcaption>Transaction from an HDFC bank statement without the sensitive information.</figcaption></figure><p>In the image, we can see that particular can be in multiple lines but belong to the same row. Tabula cannot differentiate whether multiple lines in a row belong to the same row. It will treat them as multiple rows, and as a result, we get the following output:</p><pre><em># First line read from HDFC statement</em><br><strong>[&#39;22/06/17&#39;, &#39;IMPS-7-RAHUL-HDFC-XXXXXXXX&#39;, &#39;XXXX7&#39;, &#39;22/06/17&#39;, nan, &#39;1,000.00&#39;, &#39;14,904.08&#39;]</strong></pre><pre><em># Second line read from HDFC statement</em><br><strong>[nan, &#39;8-XXXX&#39;, nan, nan, nan, nan, nan] </strong></pre><p><strong>Aim:</strong> Convert the same particular from multiple rows into one.</p><p><strong>Solution:</strong> The first line of every entry contains particular, date, balance, transaction amount and cheque number. Only the particulars can be multiline. So, a multiline particular can be between two date entries.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7e5c4b421218ee8205ea72cb555dcad7/href">https://medium.com/media/7e5c4b421218ee8205ea72cb555dcad7/href</a></iframe><h3>Credit, debit and default</h3><p>As we saw in the columns of various bank statements, the differentiation between credit, debit and default is based on whether the entry is in deposit column or withdrawal column or in some cases whether mentioned as CR/DR.</p><p><strong>Aim:</strong> Classify every transaction as credit, debit or default.</p><p><strong>Solution:</strong> Classify all deposits as <em>credit </em>and withdrawals as <em>debit</em>. An event of <em>default</em> is defined when a withdrawal leads to negative closing balance and then immediately followed by a deposit of the same amount.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/934/1*HpGooSu9f6SK0WlP09plxQ.png" /><figcaption>Example of a default transaction</figcaption></figure><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4ae5bcb990be3405c9bc8b51bb4cbd0a/href">https://medium.com/media/4ae5bcb990be3405c9bc8b51bb4cbd0a/href</a></iframe><h3>Categorising transactions</h3><p>To perform analysis on the bank transactions, we need to categorise every bank transaction. Categorizing enables us to perform category specific operations and answer questions such as “how much does he spend on operations?” or “what are the different channels of earning?”. A category can be ATM, Shopping, IMPS, NEFT, etc.</p><p><strong>Aim:</strong> Categorise every transaction.</p><p><strong>Solution:</strong> For every transaction, tokenise the particular and based on the occurrence and position of keywords assign a category.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d3cfbf69a48780c5156bfa02f0fc396c/href">https://medium.com/media/d3cfbf69a48780c5156bfa02f0fc396c/href</a></iframe><h3>Cashflow analysis</h3><p>Now that we have read, cleaned and categorised transactions from the bank statement, it’s time to generate some insights. After all, what’s data without information?</p><p>Cashflow analysis helps in</p><ol><li>Analyzing the spending, buying and saving behaviour of the user</li><li>Checking whether the user is doing any side business</li><li>Calculating growth of the business</li><li>Checking whether user has any running loans and their payment status</li><li>Calculating repaying capacity of the user</li><li>Analyzing recurring transactions</li></ol><p><strong>Overall analysis</strong></p><p>This analysis gives an overall view of the total number and amount of credits, debits and defaults in the bank statement. Also contains a categorical breakdown of cash and non-cash transactions.</p><p><strong>Monthly analysis</strong></p><p>This analysis is a month-wise breakdown of the overall analysis of the bank statement. Helps in calculating the growth of the business.</p><p><strong>Defaults analysis</strong></p><p>This analysis shows the total number and amount of defaults in the bank statement along with the details of every default.</p><p><strong>Recurring transactions</strong></p><p>To understand the spending behaviour of the user we need to know the most common transactions. To answer questions like, “Are there multiple NEFT transactions to/from the same person/company?”, “Is he an IRCTC agent?” etc., We used <a href="https://xlinux.nist.gov/dads/HTML/ratcliffObershelp.html">Ratcliff-Obershelp</a> algorithm to club similar transactions with more than 85% similarity. For better results, removed numbers, special characters from the strings.</p><blockquote><strong>Note:</strong> Code snippets mentioned above are pseudocodes to demonstrate the idea and may not contain all the edge cases.</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7470bffbe5e2" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>