Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(305172): add MobSF security scan on android and iOS builds for staging and prod #37

Merged
merged 1 commit into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions build/azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ stages:
applicationEnvironment: Staging
deploymentEnvironment: TestFlight

# MOBSF SECURITY SCAN STAGING
- stage: Security_Scan_Build_Staging
dependsOn: Build_Staging
jobs:
- template: stage-security-scan.yml
parameters:
applicationEnvironment: Staging
enableIosSecurityScan: true
enableAndroidSecurityScan: true

- stage: Build_Production
dependsOn: Build_Staging
condition: and(succeeded(), eq(variables['IsPullRequestBuild'], 'false'))
Expand Down Expand Up @@ -138,6 +148,16 @@ stages:
appCenterServiceConnectionName: $(AppCenterServiceConnection)
appCenterDistributionGroup: $(AppCenterDistributionGroup)

# MOBSF SECURITY SCAN PRODUCTION
- stage: Security_Scan_Build_Production
dependsOn: Build_Production
jobs:
- template: stage-security-scan.yml
parameters:
applicationEnvironment: Production
enableIosSecurityScan: true
enableAndroidSecurityScan: true

- stage: AppStore
condition: and(succeeded(), eq(variables['IsPullRequestBuild'], 'false'))
dependsOn: Build_Production
Expand Down
38 changes: 38 additions & 0 deletions build/stage-security-scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
parameters:
- name: applicationEnvironment
type: string
default: ''
- name: enableIosSecurityScan
type: boolean
default: false
- name: enableAndroidSecurityScan
type: boolean
default: false

jobs:
- job: OnLinux_iOS_SecurityScan
condition: eq(${{parameters.enableIosSecurityScan}}, true)
dependsOn: []
pool:
vmImage: $(ubuntuHostedAgentImage)
steps:
- template: templates/mobsf-scan.yml
parameters:
platform: 'iOS'
fileExtension: $(ipaFileExtension)
mobSfApiKey: $(mobSfApiKey)
artifactName: '$(iOSArtifactName)_${{ parameters.applicationEnvironment }}'

- job: OnLinux_Android_SecurityScan
condition: eq(${{parameters.enableAndroidSecurityScan}}, true)
dependsOn: []
pool:
vmImage: $(ubuntuHostedAgentImage)
steps:
- template: templates/mobsf-scan.yml
parameters:
platform: 'android'
fileExtension: $(aabFileExtension)
mobSfApiKey: $(mobSfApiKey)
artifactName: '$(AndroidArtifactName)_${{ parameters.applicationEnvironment }}'

6 changes: 3 additions & 3 deletions build/steps-build-ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ steps:
bannerVersionNumberText: '$(MajorMinorPatch)'

- task: FlutterBuild@0
displayName: Build Project for Release
displayName: Build Project for Release
inputs:
target: 'ios'
buildName: '$(MajorMinorPatch)'
Expand All @@ -103,7 +103,7 @@ steps:
condition: failed()

- task: Xcode@5
condition: and(succeeded(), eq(variables['IsPullRequestBuild'], 'false'))
carlh98 marked this conversation as resolved.
Show resolved Hide resolved
condition: succeeded()
displayName: 'Xcode Archive iOS'
inputs:
actions: 'archive'
Expand All @@ -122,7 +122,7 @@ steps:
provisioningProfileName: '$(provisioningProfile.provisioningProfileName)'

- task: CopyFiles@2
condition: and(succeeded(), eq(variables['IsPullRequestBuild'], 'false'))
carlh98 marked this conversation as resolved.
Show resolved Hide resolved
condition: succeeded()
displayName: 'Copy Binary Files'
inputs:
sourceFolder: '${{ parameters.pathToSrc }}/app'
Expand Down
149 changes: 149 additions & 0 deletions build/templates/mobsf-scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
parameters:
- name: mobSfApiKey
type: string
default: $(mobSfApiKey)
- name: artifactName
type: string
default: ''
- name: fileExtension
type: string
default: ''
- name: platform
type: string
default: ''

steps:
- task: DownloadPipelineArtifact@1
inputs:
buildType: 'current'
downloadType: 'single'
artifactName: "${{parameters.artifactName}}"
downloadPath: '$(System.ArtifactsDirectory)'
condition: succeeded()

- task: DockerInstaller@0
displayName: 'Install Docker'
inputs:
dockerVersion: $(DockerVersion)
condition: succeeded()

- script: docker pull opensecurity/mobile-security-framework-mobsf:latest
displayName: 'Pull MobSF Docker Image'
condition: succeeded()

- script: docker run -d -it --rm -e MOBSF_API_KEY='${{parameters.mobSfApiKey}}' -e DATA_UPLOAD_MAX_MEMORY_SIZE=209715200 -p 8000:8000 opensecurity/mobile-security-framework-mobsf:latest
displayName: 'Run MobSF Docker Image'
condition: succeeded()

- script: ls -R "$(System.ArtifactsDirectory)"
displayName: 'List Downloaded Artifacts'

- task: PowerShell@2
displayName: 'Upload file to MobSF'
inputs:
targetType: 'inline'
script: |
$FILENAME = Get-ChildItem -Path "$(System.ArtifactsDirectory)/" -File | Select-Object -First 1 -ExpandProperty Name
Write-Host "Filename: $FILENAME"
$file = "$(System.ArtifactsDirectory)/$FILENAME"
Write-Host "Uploading file $file to MobSF..."

# Check if the server is up and running
$serverIsRunning = $false
$retryCount = 0
while (-not $serverIsRunning -and $retryCount -lt 10) {
$response = curl --url http://127.0.0.1:8000/api/v1/scans -H "Authorization: ${{parameters.mobSfApiKey}}"
if ($response.StatusCode -eq 200) {
Write-Host "MobSF server is up and running"
$serverIsRunning = $true
} else {
Write-Host "Waiting for the MobSF server to start..."
$retryCount++
Start-Sleep -Seconds 5
}
}

$uploadResponse = curl -X POST -F "file=@$file;type=application/octet-stream" http://127.0.0.1:8000/api/v1/upload -H "Authorization: ${{parameters.mobSfApiKey}}"
$uploadResponseJson = $uploadResponse | ConvertFrom-Json
$hash = $uploadResponseJson.hash
echo "##vso[task.setvariable variable=fileHash]$hash"
Write-Host "Uploaded file hash: $hash"
Write-Host "File uploaded to MobSF successfully"
condition: succeeded()

- task: PowerShell@2
displayName: 'Run MobSF Scan'
inputs:
targetType: 'inline'
script: |
Write-Host "MobSF Scan started"
$hash = "$(fileHash)"
$scanUrl = "http://127.0.0.1:8000/api/v1/scan"
Write-Host "Scanning Uploaded file hash: $hash"
$headers = @{
"Authorization" = ${{parameters.mobSfApiKey}}
}
$scanBody = @{
"scan_type" = "${{parameters.fileExtension}}"
"hash" = $hash # use the hash from the upload response
}
$response = Invoke-RestMethod -Uri $scanUrl -Method Post -Headers $headers -Body $scanBody
Write-Host "Scan completed"
condition: succeeded()

- task: PowerShell@2
displayName: 'Download MobSF PDF report'
inputs:
targetType: 'inline'
script: |
Write-Host "Downloading MobSF PDF report..."
$hash = "$(fileHash)"
Write-Host "File hash: $hash"
$reportUrl = "http://127.0.0.1:8000/api/v1/download_pdf"
$headers = @{
"Authorization" = "${{parameters.mobSfApiKey}}"
}
$reportBody = @{
"hash" = $hash # use the hash from the upload response
}
$directoryPath = "${{parameters.artifactName}}/${{parameters.platform}}"
if (!(Test-Path -Path $directoryPath)) {
New-Item -ItemType Directory -Path $directoryPath | Out-Null
}
$pdfFilePath = "$directoryPath/report.pdf"
Invoke-WebRequest -Uri $reportUrl -Method Post -Headers $headers -Body $reportBody -OutFile $pdfFilePath
Write-Host "PDF report downloaded to $pdfFilePath"
condition: succeeded()

- task: PowerShell@2
displayName: 'Download MobSF Json report'
inputs:
targetType: 'inline'
script: |
Write-Host "Downloading MobSF Json report..."
$hash = "$(fileHash)"
Write-Host "File hash: $hash"
$reportUrl = "http://127.0.0.1:8000/api/v1/report_json"
$headers = @{
"Authorization" = "${{parameters.mobSfApiKey}}"
}
$reportBody = @{
"hash" = $hash # use the hash from the upload response
}
$reportResponse = Invoke-RestMethod -Uri $reportUrl -Method Post -Headers $headers -Body $reportBody
Write-Host "Response: $reportResponse"
$jsonFilePath = "${{parameters.artifactName}}/${{parameters.platform}}/Report.json"
$reportResponse | ConvertTo-Json | Out-File -FilePath $jsonFilePath
Write-Host "JSON report downloaded to $jsonFilePath"
condition: succeeded()

- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact'
inputs:
PathtoPublish: '${{parameters.artifactName}}/${{parameters.platform}}'
ArtifactName: ${{parameters.artifactName}}_Security_Reports
publishLocation: 'Container'

- task: PostBuildCleanup@3
displayName: 'Post-Build Cleanup: Cleanup files to keep build server clean!'
condition: always()
17 changes: 16 additions & 1 deletion build/variables.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,17 @@
InternalKeyProperties: com.nventive.internal.flutterapptemplate.key.properties # This is the internal key properties used for internal builds.
GooglePlayKeystore: com.nventive.flutterapptemplate.jks # This is the official keystore used for Google Play.
GooglePlayKeyProperties: com.nventive.flutterapptemplate.key.properties # This is the official key properties used for Google Play.
aabFileExtension: aab # This is the file extension for Android App Bundles.

# iOS.
InternalProvisioningProfile: com.nventive.internal.flutterapptemplate.mobileprovision # This is the internal provisioning profile for internal builds.
InternalExportOptions: com.nventive.internal.flutterapptemplate.exportOptions.plist # This is the export options file for internal builds.
InternalCertificate: nventive.p12 # This is the certificate from the nventive Apple account used to sign internal builds.
AppStoreProvisioningProfile: com.nventive.flutterapptemplate.mobileprovision # This is the client provisioning profile for the AppStore (Production distribution).
AppStoreExportOptions: com.nventive.flutterapptemplate.exportOptions.plist # This is the export options file for the AppStore (Production distribution) builds.
AppStoreCertificate: nventive.p12 # This is the client production certificate used to sign AppStore builds.

ipaFileExtension: ipa # This is the file extension for iOS IPA files.

# Firebase
InternalFirebaseJson: firebase-flutter-internal.json
InternalFirebaseOptionsDart: firebase_options-flutter-internal.dart
Expand Down Expand Up @@ -69,9 +72,21 @@
# Flutter version.
FlutterVersion: '3.22.1'

# Docker container to run MobSF.
DockerVersion: '25.0.5'

# MobSF Auth key.
# Corrresponds to the key used for authenticating requests to the MobSF API. In this pipeline setup, MobSF runs in a docker container and by
# default the API key is randomly generated by MobSF itself when running it in a dedicated server, but because we're automating the process
# we need to manually set it beforehand so we can authenticate further requests. Can be set to any string. More info:
# - https://mobsf.github.io/docs/#/extras?id=extra-features
# - https://mobsf.live/api_docs
mobSfApiKey: '8181'
Arieldelossantos marked this conversation as resolved.
Show resolved Hide resolved

# Virtual machine images.
windowsHostedAgentImage: 'windows-2022'
macOSHostedAgentImage: 'macOS-13'
ubuntuHostedAgentImage: 'ubuntu-22.04'

# Name of the folder where the artefacts will be placed. Variable used in build and release phases.
# We make seperate folders so that releases can each download only the folder they need.
Expand Down
53 changes: 53 additions & 0 deletions doc/SecurityScan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# MobSF Security scan integration

## stage-security-scan.yml

This YAML configuration snippet outlines the setup for conditional security scanning within a CI/CD pipeline, specifically targeting iOS and Android platforms. The security scans are facilitated through the Mobile Security Framework (MobSF), a comprehensive tool designed for mobile security testing.

## Configuration Parameters
- **applicationEnvironment:** Specifies the deployment environment of the application. This string parameter helps in distinguishing artifacts by appending the environment name to the artifact's name.

- **enableIosSecurityScan:** A boolean parameter that, when set to true, enables security scanning for iOS applications.

- **enableAndroidSecurityScan:** Similar to enableIosSecurityScan, this boolean parameter enables security scanning for Android applications when set to true.

## Jobs Description
### iOS Security Scan Job (*OnLinux_iOS_SecurityScan*)
This job is designed to perform security scans on iOS applications. It is conditionally executed based on the `enableIosSecurityScan` parameter.

- **Condition for Execution:** The job runs if `enableIosSecurityScan` is set to true.
- **Execution Environment:** The job is executed on a Linux-based virtual machine, specified by the ubuntuHostedAgentImage variable.
- **Key Steps:**
- **Security Scan:** Utilizes a predefined template (`mobsf-scan.yml`) for conducting the security scan.
- **Parameters Passed:**
- `platform`: Specify the platform you'll be scanning.
- `fileExtension`: Required to specify the file extension MobSF will scan.
- `mobSfApiKey`: A key required for accessing MobSF services.
- `artifactName`: The name of the iOS artifact, appended with the specified applicationEnvironment.

### Android Security Scan Job (*OnLinux_Android_SecurityScan*)
This job mirrors the iOS security scan job but targets Android applications. It is also conditionally executed based on a specific parameter.

- **Condition for Execution:** The job runs if `enableAndroidSecurityScan` is set to true.
- **Execution Environment:** Similar to the iOS job, it runs on a Linux-based virtual machine specified by the ubuntuHostedAgentImage variable.
- **Key Steps:**
- **Security Scan:** Executes a predefined template (`mobsf-scan.yml`) for the Android security scan.
- **Parameters Passed:**
- `platform`: Specify the platform you'll be scanning.
- `fileExtension`: Specify the file extension MobSF will scan.
- `mobSfApiKey`: Required key for MobSF services.
- `artifactName`: The name of the Android artifact, appended with the specified applicationEnvironment.

## Usage
### How to Add the the new Stage for Security Scan

> 1. Create a new `stage`. (*stage: Security_Scan_Build_[Environment]*)
> 2. Configure the `dependsOn` property to the stage is creating the build, like `Build_Staging` and `Build_Production`.
> 3. Add the job we want to run using the new security template
> 4. Set the parameters `applicationEnvironment`, `enableIosSecurityScan`, `enableAndroidSecurityScan`

Integrating the security scanning configuration into your `azure-pipelines.yml` pipeline requires activating specific parameters to enable the process. Set `enableIosSecurityScan` and `enableAndroidSecurityScan` to **true** as per your project's requirements. This approach ensures a tailored security assessment for mobile applications across different environments, in this case Staging and Production, utilizing **MobSF** for comprehensive vulnerability detection prior to deployment.

## References
- [MobSF Documentation](https://mobsf.github.io/docs/#/)
- [MobSF API Docs](https://mobsf.live/api_docs)
5 changes: 4 additions & 1 deletion src/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# Changelog
# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)

Prefix your items with `(Template)` if the change is about the template and not the resulting application.

## 0.20.0
- Configured MobSF security scan on Android and iOS for Staging and Production builds.
Arieldelossantos marked this conversation as resolved.
Show resolved Hide resolved

## 0.19.4
- Fix CI/CD artifact name for iOS stage.

Expand Down