-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 3d5ed87
Showing
10 changed files
with
435 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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! 🚀 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
Large diffs are not rendered by default.
Oops, something went wrong.
Binary file not shown.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.