Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
AswathyAsokan-opsmx committed Dec 23, 2024
0 parents commit 3d5ed87
Show file tree
Hide file tree
Showing 10 changed files with 435 additions and 0 deletions.
65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# MobSF Static Scanning of App

## Overview

This Go project provides a core setup flow for performing static scanning of Android and iOS app packages using MobSF (Mobile Security Framework). It is designed to simplify the process of scanning mobile applications for security vulnerabilities.

## Prerequisites

- Go installed on your machine
- Kubernetes cluster set up
- `kubectl` configured to interact with your cluster
- MobSF installed and configured

## Getting Started

Follow these steps to set up and run the MobSF static scanning:

### Step 1: Navigate to the Project Directory

<!-- ```bash -->
cd mobsf

### Step 2: Update the API Key

Replace your 64-character alpha numeric API key with both lower and upper case in the appropriate YAML configuration file:

- For development: `build/mobsf-dev.yaml`
- For production: `build/mobsf-prod.yaml`

### Step 3: Update Namespace (if required)

If you need to change the namespace, do so in the YAML configuration files as needed.

### Step 4: Deploy the Configuration

Run the following command based on the environment type (dev or prod):

<!-- ```bash -->
kubectl apply -f build/mobsf-dev.yaml

Note: For this project, we will be using the dev setup in the mobsf namespace.

### Step 5: Get the NodePort

After deploying, pick the NodePort from the deployed service to access the MobSF server.

### Step 6: Update MobSF Server URL and App Path

Replace the MobSF server URL and the app path in the code with the correct details that correspond to your setup.

### Step 7: Clean Up Dependencies

Run the following command to tidy up your Go module dependencies:

<!-- ```bash -->
go mod tidy

### Step 8: Run the Application

Finally, execute the main Go program:

<!-- ```bash -->
go run main.go

Happy scanning! 🚀
41 changes: 41 additions & 0 deletions build/mobsf-dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
apiVersion: v1
kind: Namespace
metadata:
name: mobsf
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mobsf-deployment
namespace: mobsf
spec:
replicas: 1
selector:
matchLabels:
app: mobsf
template:
metadata:
labels:
app: mobsf
spec:
containers:
- name: mobsf
image: opensecurity/mobile-security-framework-mobsf:latest
env:
- name: MOBSF_API_KEY
value: "d7bf5e5eaf31a764bddff2dd38ba87f6611d0e99b0a29f16a75d5dbc30b90651"
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: mobsf-service
namespace: mobsf
spec:
type: NodePort
selector:
app: mobsf
ports:
- port: 8000
targetPort: 8000
41 changes: 41 additions & 0 deletions build/mobsf-prod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
apiVersion: v1
kind: Namespace
metadata:
name: mobsf
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mobsf-deployment
namespace: mobsf
spec:
replicas: 1
selector:
matchLabels:
app: mobsf
template:
metadata:
labels:
app: mobsf
spec:
containers:
- name: mobsf
image: opensecurity/mobile-security-framework-mobsf:latest
env:
- name: MOBSF_API_KEY
value: "Create1NewSecretKeyWith64CharsAlphaNumericUpperAndLower"
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: mobsf-service
namespace: mobsf
spec:
type: ClusterIP
selector:
app: mobsf
ports:
- port: 8000
targetPort: 8000
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/opsmx/ssd-mobile-scanning

go 1.22.5
Empty file added go.sum
Empty file.
1 change: 1 addition & 0 deletions reports/analysis_aswathy_owncloud_101_codescan_mobsf.json

Large diffs are not rendered by default.

Binary file not shown.

Large diffs are not rendered by default.

242 changes: 242 additions & 0 deletions static/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
package static

import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/url"
"os"
"strings"
)

// StaticScanClient defines the interface for interacting with the scanning service.
type StaticScanClient interface {
UploadApp(filePath string) (*UploadResult, error)
TriggerScan(hash string) ([]byte, error)
GetScanLogs(hash string) (*ScanLogsResult, error)
GetJsonReport(hash string) ([]byte, error)
GetPdfReport(hash string) ([]byte, error)
DeleteScan(hash string) (*DeleteResult, error)
}

// apiClient implements the StaticScanClient interface.
type apiClient struct {
baseURL string // Base URL for the scanning service API
apiToken string // Authorization token for API access
}

// NewClient creates and returns a new instance of StaticScanClient.
func NewClient(baseURL, apiToken string) StaticScanClient {
return &apiClient{
baseURL: baseURL,
apiToken: apiToken,
}
}

// UploadApp uploads a file to the scanning service.
// Returns the upload result containing the file's unique hash.
func (c *apiClient) UploadApp(filePath string) (*UploadResult, error) {
// Prepare multipart form data
buf := &bytes.Buffer{}
mpw := multipart.NewWriter(buf)

// Open the file to be uploaded
file, err := os.Open(filePath)
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
defer file.Close()

// Add the file to the form data
fWriter, err := mpw.CreateFormFile("file", file.Name())
if err != nil {
return nil, fmt.Errorf("failed to create form file: %w", err)
}
if _, err = io.Copy(fWriter, file); err != nil {
return nil, fmt.Errorf("failed to copy file data: %w", err)
}

// Close the multipart writer
if err := mpw.Close(); err != nil {
return nil, fmt.Errorf("failed to close multipart writer: %w", err)
}

// Create HTTP request
req, err := http.NewRequest(http.MethodPost, c.baseURL+"/api/v1/upload", buf)
if err != nil {
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
}
req.Header.Set("Authorization", c.apiToken)
req.Header.Set("Content-Type", mpw.FormDataContentType())

// Execute the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("HTTP request failed: %w", err)
}
defer resp.Body.Close()

// Handle response
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("upload failed: %s", resp.Status)
}
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}

// Parse response JSON
result := &UploadResult{}
if err := json.Unmarshal(respBody, result); err != nil {
return nil, fmt.Errorf("failed to parse response JSON: %w", err)
}
return result, nil
}

// TriggerScan initiates a scan for the uploaded file using its hash.
// Returns the scan response upon completion.
func (c *apiClient) TriggerScan(hash string) ([]byte, error) {
data := url.Values{}
data.Set("hash", hash)

// Create HTTP request
req, err := http.NewRequest(http.MethodPost, c.baseURL+"/api/v1/scan", strings.NewReader(data.Encode()))
if err != nil {
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
}
req.Header.Set("Authorization", c.apiToken)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

// Execute the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("HTTP request failed: %w", err)
}
defer resp.Body.Close()

// Handle response
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("scan trigger failed: %s", resp.Status)
}
return io.ReadAll(resp.Body)
}

// GetScanLogs fetches logs for the ongoing or completed scan using its hash.
func (c *apiClient) GetScanLogs(hash string) (*ScanLogsResult, error) {
data := url.Values{}
data.Set("hash", hash)

// Create HTTP request
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/v1/scan_logs", c.baseURL), strings.NewReader(data.Encode()))
if err != nil {
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
}
req.Header.Set("Authorization", c.apiToken)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

// Execute the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("HTTP request failed: %w", err)
}
defer resp.Body.Close()

// Handle response
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get scan logs: %s", resp.Status)
}
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}

// Parse response JSON
result := &ScanLogsResult{}
if err := json.Unmarshal(respBody, result); err != nil {
return nil, fmt.Errorf("failed to parse response JSON: %w", err)
}
return result, nil
}

// GetJsonReport fetches the JSON report for a completed scan using its hash.
func (c *apiClient) GetJsonReport(hash string) ([]byte, error) {
return c.getReport(hash, "/api/v1/report_json")
}

// GetPdfReport fetches the PDF report for a completed scan using its hash.
func (c *apiClient) GetPdfReport(hash string) ([]byte, error) {
return c.getReport(hash, "/api/v1/download_pdf")
}

// getReport is a helper function to fetch reports from the specified endpoint.
func (c *apiClient) getReport(hash, endpoint string) ([]byte, error) {
data := url.Values{}
data.Set("hash", hash)

// Create HTTP request
req, err := http.NewRequest(http.MethodPost, c.baseURL+endpoint, strings.NewReader(data.Encode()))
if err != nil {
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
}
req.Header.Set("Authorization", c.apiToken)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

// Execute the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("HTTP request failed: %w", err)
}
defer resp.Body.Close()

// Handle response
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to fetch report: %s", resp.Status)
}
return io.ReadAll(resp.Body)
}

// DeleteScan deletes the scan data using its hash.
// Returns the result of the deletion.
func (c *apiClient) DeleteScan(hash string) (*DeleteResult, error) {
data := url.Values{}
data.Set("hash", hash)

// Create HTTP request
req, err := http.NewRequest(http.MethodPost, c.baseURL+"/api/v1/delete_scan", strings.NewReader(data.Encode()))
if err != nil {
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
}
req.Header.Set("Authorization", c.apiToken)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

// Execute the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("HTTP request failed: %w", err)
}
defer resp.Body.Close()

// Handle response
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to delete scan: %s", resp.Status)
}
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}

// Parse response JSON
result := &DeleteResult{}
if err := json.Unmarshal(respBody, result); err != nil {
return nil, fmt.Errorf("failed to parse response JSON: %w", err)
}
return result, nil
}
Loading

0 comments on commit 3d5ed87

Please sign in to comment.