Documentation
¶
Overview ¶
Package mobilepkg provides a unified API for inspecting and analyzing Android (APK/XAPK/APKS/AAB) and iOS (IPA) mobile application packages.
It abstracts platform-specific differences so that callers can perform common operational tasks with a single set of API calls:
- CI quality gates: validate identity, version, and permissions
- Security inspection: detect debuggable builds, exposed components, dangerous permissions
- Release diff: compare two packages to detect changes
- Catalog extraction: collect display name, icon, and platform info
The primary entry point is InspectFile, which performs a complete inspection in a single call — extracting package metadata, running analysis, and returning a unified InspectResult:
result, err := mobilepkg.InspectFile(ctx, "app.apk")
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Identity.Identifier) // "com.example.app"
for _, f := range result.Findings {
fmt.Printf("[%s] %s\n", f.Severity, f.Message)
}
For CI fail conditions, use Check:
verdict := mobilepkg.Check(result, mobilepkg.DefaultFailPolicy())
For release comparison, use Compare:
diff := mobilepkg.Compare(oldResult, newResult)
For custom validation rules, use Validate:
violations := mobilepkg.Validate(result, rules)
For expert use, InspectFileWithOptions accepts archive safety limits.
Index ¶
- Constants
- Variables
- func WriteRDJSONL(w io.Writer, findings []Finding, archivePath string) error
- func WriteReportJSON(w io.Writer, rf ReportFile) error
- func WriteSummaryMarkdown(w io.Writer, rf ReportFile) error
- type ArchiveLimits
- type CertSummary
- type Confidence
- type DataSpec
- type Diagnostic
- type Diff
- type DomainConfig
- type EntryPoint
- type Evidence
- type ExportedComponent
- type FailPolicy
- type FailResult
- type Finding
- type Format
- type IconAsset
- type Identity
- type InspectError
- type InspectOptions
- type InspectResult
- func Inspect(ctx context.Context, r io.ReaderAt, size int64) (*InspectResult, error)
- func InspectFile(ctx context.Context, path string) (*InspectResult, error)
- func InspectFileWithOptions(ctx context.Context, path string, opts InspectOptions) (*InspectResult, error)
- func InspectWithBaseline(ctx context.Context, path string, baseline *InspectResult) (*InspectResult, error)
- type IntentFilter
- type NetworkEndpoint
- type NetworkSecurityPolicy
- type Permission
- type Platform
- type ReportFile
- type Rule
- type RuleFunc
- type SDKConstraints
- type SecretCandidate
- type Severity
- type SigningInfo
- type Version
- type Violation
Examples ¶
Constants ¶
const SchemaVersion = "1.0.0"
SchemaVersion is the current schema version of the report.json output.
Variables ¶
var ( // ErrUnsupportedFormat is returned when the file is not a recognized // mobile application package (neither APK nor IPA). ErrUnsupportedFormat = errors.New("mobilepkg: unsupported package format") // ErrManifestMissing is returned when the primary manifest // (AndroidManifest.xml or Info.plist) is not found in the archive. ErrManifestMissing = errors.New("mobilepkg: primary manifest not found") // ErrManifestCorrupt is returned when the primary manifest exists // but cannot be parsed. ErrManifestCorrupt = errors.New("mobilepkg: primary manifest could not be parsed") // ErrArchiveCorrupt is returned when the archive structure is damaged // or unreadable beyond the initial ZIP open. ErrArchiveCorrupt = errors.New("mobilepkg: archive is corrupt or unreadable") // ErrPathTraversal is returned when a ZIP entry name contains path // traversal components such as ".." or begins with an absolute path. ErrPathTraversal = errors.New("mobilepkg: path traversal detected") // ErrTooManyFiles is returned when the archive contains more entries // than the configured [ArchiveLimits.MaxEntryCount]. ErrTooManyFiles = errors.New("mobilepkg: too many files in archive") // ErrOversize is returned when the archive or an individual entry // exceeds the configured size limits. ErrOversize = errors.New("mobilepkg: archive exceeds size limit") // ErrTooDeep is returned when nested archives exceed the configured // [ArchiveLimits.MaxNestingDepth]. ErrTooDeep = errors.New("mobilepkg: archive nesting too deep") // ErrCompressionRatioExceeded is returned when a ZIP entry's // compression ratio exceeds [ArchiveLimits.MaxCompressionRatio]. ErrCompressionRatioExceeded = errors.New("mobilepkg: compression ratio exceeded") // ErrSymlink is returned when a ZIP entry is a symlink or hardlink // and [ArchiveLimits.AllowSymlinks] is false. ErrSymlink = errors.New("mobilepkg: symlink or hardlink in archive") // ErrInvalidName is returned when a ZIP entry name contains NUL bytes, // control characters, or exceeds the configured path length limit. ErrInvalidName = errors.New("mobilepkg: invalid entry name") )
Sentinel errors returned by the public API. Use errors.Is to check for these in wrapped errors.
var ErrUnknownField = errors.New("mobilepkg: unknown field name")
ErrUnknownField is returned by RequireFields when an unrecognized field name is provided. This fail-closed behavior prevents silent misconfiguration.
Functions ¶
func WriteRDJSONL ¶
WriteRDJSONL writes findings in reviewdog's rdjsonl format to w. Each finding is written as a single JSON line. This output is intended for use with reviewdog's github-check reporter.
The archivePath parameter is used as the file path in the rdjsonl output since findings reference archive-internal paths rather than repository file paths.
func WriteReportJSON ¶
func WriteReportJSON(w io.Writer, rf ReportFile) error
WriteReportJSON writes the report as pretty-printed JSON to w.
func WriteSummaryMarkdown ¶
func WriteSummaryMarkdown(w io.Writer, rf ReportFile) error
WriteSummaryMarkdown writes a human-readable Markdown summary of the inspection result to w. The layout leads with the most actionable information — top findings and new risks — before metrics tables.
If the result includes a baseline diff (ar.Diff != nil), the summary highlights changes compared to the previous report.
Types ¶
type ArchiveLimits ¶
type ArchiveLimits struct {
// MaxInputBytes is the maximum allowed size of the input file in bytes.
// Archives larger than this are rejected before parsing.
MaxInputBytes int64
// MaxEntryCount is the maximum number of entries allowed in the archive.
MaxEntryCount int
// MaxTotalUncompressedBytes is the maximum total uncompressed size
// of all entries combined.
MaxTotalUncompressedBytes int64
// MaxSingleEntryUncompressedBytes is the maximum uncompressed size
// of any single entry.
MaxSingleEntryUncompressedBytes int64
// MaxNestingDepth is the maximum allowed depth for nested archives
// (e.g. an APK inside an XAPK). Zero means no limit on nesting depth.
MaxNestingDepth int
// MaxPathLength is the maximum allowed length (in bytes) for any
// single entry path within the archive.
MaxPathLength int
// MaxCompressionRatio is the maximum allowed ratio of uncompressed
// to compressed size for any single entry. This guards against
// compression bombs. Zero means no ratio limit.
MaxCompressionRatio float64
// AllowSymlinks controls whether symlink entries in the archive are
// accepted. When false (the default), symlinks cause a safety error.
AllowSymlinks bool
}
ArchiveLimits controls safety limits applied when reading ZIP archives. These limits protect against malicious or malformed archives such as zip bombs, path-traversal attacks, and symlink exploits.
A zero value for any numeric field means "no limit" for that check. Use DefaultArchiveLimits to obtain a set of sensible defaults.
func DefaultArchiveLimits ¶
func DefaultArchiveLimits() ArchiveLimits
DefaultArchiveLimits returns an ArchiveLimits with sensible defaults suitable for most use cases.
- MaxInputBytes: 2 GiB
- MaxEntryCount: 100,000
- MaxTotalUncompressedBytes: 4 GiB
- MaxSingleEntryUncompressedBytes: 512 MiB
- MaxNestingDepth: 4
- MaxPathLength: 512
- MaxCompressionRatio: 100
- AllowSymlinks: false
type CertSummary ¶
type CertSummary struct {
// Subject is the certificate subject (typically CN or O).
Subject string `json:"subject"`
// Issuer is the certificate issuer.
Issuer string `json:"issuer"`
// NotBefore is the certificate validity start in RFC 3339 format.
NotBefore string `json:"not_before"`
// NotAfter is the certificate validity end in RFC 3339 format.
NotAfter string `json:"not_after"`
// SHA256Fingerprint is the hex-encoded SHA-256 fingerprint of the DER certificate.
SHA256Fingerprint string `json:"sha256_fingerprint"`
// SerialNumber is the certificate serial number in decimal.
SerialNumber string `json:"serial_number"`
// SignatureAlgorithm is the algorithm used to sign the certificate (e.g. "SHA256-RSA").
SignatureAlgorithm string `json:"signature_algorithm,omitempty"`
// PublicKeyAlgorithm is the public key algorithm (e.g. "RSA", "ECDSA").
PublicKeyAlgorithm string `json:"public_key_algorithm,omitempty"`
// KeySize is the public key size in bits (e.g. 2048, 4096).
KeySize int `json:"key_size,omitempty"`
// SelfSigned is true if the certificate subject matches the issuer.
SelfSigned bool `json:"self_signed,omitempty"`
}
CertSummary holds a summary of an X.509 certificate used for code signing.
type Confidence ¶
type Confidence string
Confidence represents how certain the analysis is about a Finding.
const ( // ConfidenceHigh indicates strong certainty in the finding. ConfidenceHigh Confidence = "high" // ConfidenceMedium indicates moderate certainty in the finding. ConfidenceMedium Confidence = "medium" // ConfidenceLow indicates weak certainty; the finding may be a false positive. ConfidenceLow Confidence = "low" )
type DataSpec ¶
type DataSpec struct {
// Scheme is the URI scheme (e.g. "https", "myapp").
Scheme string `json:"scheme,omitempty"`
// Host is the hostname or authority.
Host string `json:"host,omitempty"`
// Path is the URI path.
Path string `json:"path,omitempty"`
}
DataSpec holds scheme/host/path from an Android intent-filter <data> element.
type Diagnostic ¶
type Diagnostic struct {
// Code is a machine-readable identifier (e.g. "icon.not_found", "plist.parse_failed").
Code string `json:"code"`
// Severity classifies the importance of the diagnostic.
Severity Severity `json:"severity"`
// Message is a human-readable description.
Message string `json:"message"`
// Detail carries optional machine-readable metadata associated with
// the diagnostic (e.g. {"path": "res/icon.png"} for an icon failure).
// May be nil when no additional detail is available.
Detail map[string]string `json:"detail,omitempty"`
}
Diagnostic describes a non-fatal issue encountered during inspection.
type Diff ¶
type Diff struct {
// OldPlatform is the platform of the old report.
OldPlatform Platform `json:"old_platform"`
// NewPlatform is the platform of the new report.
NewPlatform Platform `json:"new_platform"`
// IdentityChanged is true when the identity fields differ.
IdentityChanged bool `json:"identity_changed"`
// VersionChanged is true when the version fields differ.
VersionChanged bool `json:"version_changed"`
// EntryChanged is true when the entry point fields differ.
EntryChanged bool `json:"entry_changed"`
// AddedPermissions lists permissions present in the new report but not the old.
AddedPermissions []Permission `json:"added_permissions,omitempty"`
// RemovedPermissions lists permissions present in the old report but not the new.
RemovedPermissions []Permission `json:"removed_permissions,omitempty"`
// AddedComponents lists exported components present in the new report but not the old.
AddedComponents []ExportedComponent `json:"added_components,omitempty"`
// RemovedComponents lists exported components present in the old report but not the new.
RemovedComponents []ExportedComponent `json:"removed_components,omitempty"`
// AddedEndpoints lists network endpoints present in the new report but not the old.
AddedEndpoints []NetworkEndpoint `json:"added_endpoints,omitempty"`
// RemovedEndpoints lists network endpoints present in the old report but not the new.
RemovedEndpoints []NetworkEndpoint `json:"removed_endpoints,omitempty"`
}
Diff represents the structured differences between two report values.
func Compare ¶
func Compare(oldIR, newIR *InspectResult) Diff
Compare compares two InspectResult values and returns a Diff describing what changed between them.
This is useful for release-diff scenarios where a release manager wants to detect added/removed permissions, version bumps, exported component changes, or new network endpoints between two builds.
Example ¶
ExampleCompare demonstrates comparing two inspect results.
package main
import (
"fmt"
"github.com/nao1215/mobilepkg"
)
func main() {
oldResult := &mobilepkg.InspectResult{
Platform: mobilepkg.PlatformAndroid,
Identity: mobilepkg.Identity{
Identifier: "com.example.app",
DisplayName: "My App",
},
Version: mobilepkg.Version{
Marketing: "1.0.0",
Build: "1",
},
}
newResult := &mobilepkg.InspectResult{
Platform: mobilepkg.PlatformAndroid,
Identity: mobilepkg.Identity{
Identifier: "com.example.app",
DisplayName: "My App",
},
Version: mobilepkg.Version{
Marketing: "2.0.0",
Build: "5",
},
}
diff := mobilepkg.Compare(oldResult, newResult)
fmt.Println("Identity changed:", diff.IdentityChanged)
fmt.Println("Version changed:", diff.VersionChanged)
}
Output: Identity changed: false Version changed: true
Example (Permissions) ¶
ExampleCompare_permissions demonstrates detecting permission changes.
package main
import (
"fmt"
"github.com/nao1215/mobilepkg"
)
func main() {
oldResult := &mobilepkg.InspectResult{
Permissions: []mobilepkg.Permission{
{Canonical: "camera", RawName: "android.permission.CAMERA", Source: "manifest"},
{Canonical: "network", RawName: "android.permission.INTERNET", Source: "manifest"},
},
}
newResult := &mobilepkg.InspectResult{
Permissions: []mobilepkg.Permission{
{Canonical: "camera", RawName: "android.permission.CAMERA", Source: "manifest"},
{Canonical: "location", RawName: "android.permission.ACCESS_FINE_LOCATION", Source: "manifest"},
},
}
diff := mobilepkg.Compare(oldResult, newResult)
for _, p := range diff.AddedPermissions {
fmt.Printf("+ %s (%s)\n", p.Canonical, p.RawName)
}
for _, p := range diff.RemovedPermissions {
fmt.Printf("- %s (%s)\n", p.Canonical, p.RawName)
}
}
Output: + location (android.permission.ACCESS_FINE_LOCATION) - network (android.permission.INTERNET)
type DomainConfig ¶
type DomainConfig struct {
// Domains lists the domains this config applies to.
Domains []string `json:"domains"`
// CleartextPermitted indicates whether cleartext traffic is allowed
// for these domains.
CleartextPermitted bool `json:"cleartext_permitted"`
// HasPinSet is true if this domain config includes certificate pinning.
HasPinSet bool `json:"has_pin_set"`
// NestedConfigs holds nested domain-config entries within this domain-config.
NestedConfigs []DomainConfig `json:"nested_configs,omitempty"`
}
DomainConfig represents a <domain-config> entry in network_security_config.xml.
type EntryPoint ¶
type EntryPoint struct {
// Kind classifies the entry point ("activity", "executable", or "unknown").
Kind string `json:"kind"`
// Name is the fully qualified name of the entry point.
Name string `json:"name"`
}
EntryPoint describes the main entry point of the application.
type Evidence ¶
type Evidence struct {
// ArchivePath is the path of the file inside the archive where the
// evidence was found (e.g. "AndroidManifest.xml").
ArchivePath string `json:"archive_path"`
// Field identifies a logical field or key within the file
// (e.g. "android:permission", "NSCameraUsageDescription").
Field string `json:"field,omitempty"`
// MatchedTextMasked is the matched value with sensitive parts masked.
MatchedTextMasked string `json:"matched_text_masked,omitempty"`
// Line is the line number within the file where the evidence was found.
// Zero means the line is unknown or not applicable.
Line int `json:"line,omitempty"`
// Offset is the byte offset within the file where the evidence was found.
// Zero means the offset is unknown or not applicable.
Offset int `json:"offset,omitempty"`
}
Evidence represents a piece of supporting evidence for a Finding. It locates the relevant data within the analyzed archive.
type ExportedComponent ¶
type ExportedComponent struct {
// Kind is the component type: "activity", "service", "receiver", or "provider".
Kind string `json:"kind"`
// Name is the fully qualified component name.
Name string `json:"name"`
// Exported indicates whether the component is exported.
Exported bool `json:"exported"`
// Permission is the permission required to access this component, if any.
Permission string `json:"permission,omitempty"`
// Authorities holds the content provider authorities (provider only).
Authorities string `json:"authorities,omitempty"`
// IntentFilters holds parsed intent-filter data for the component.
IntentFilters []IntentFilter `json:"intent_filters,omitempty"`
// ReadPermission is the permission required to read from this provider.
ReadPermission string `json:"read_permission,omitempty"`
// WritePermission is the permission required to write to this provider.
WritePermission string `json:"write_permission,omitempty"`
// GrantURIPermissions indicates if URI permissions can be granted.
GrantURIPermissions bool `json:"grant_uri_permissions,omitempty"`
}
ExportedComponent represents an application component that is accessible to other applications on the device. On Android this includes activities, services, broadcast receivers, and content providers that are explicitly or implicitly exported.
type FailPolicy ¶
type FailPolicy struct {
// FailOnSeverity fails if any finding has severity >= this value.
// Empty string means severity is not checked.
FailOnSeverity Severity `json:"fail_on_severity,omitempty"`
// FailOnConfidence fails if any finding has confidence >= this value.
// Empty string means confidence is not checked.
FailOnConfidence Confidence `json:"fail_on_confidence,omitempty"`
// NewOnly restricts fail checks to new findings only. When true and
// a baseline is provided to [EvaluateFailConditions], only findings
// not present in the baseline trigger failure. Has no effect when
// used with [Check] (which has no baseline).
NewOnly bool `json:"new_only,omitempty"`
}
FailPolicy configures the conditions under which an inspection is considered failed. A finding triggers failure if it meets ANY of the specified thresholds (OR logic). Use Check or EvaluateFailConditions to produce a pass/fail verdict.
func DefaultFailPolicy ¶
func DefaultFailPolicy() FailPolicy
DefaultFailPolicy returns a FailPolicy suitable for CI use:
- fail_on_severity: warn (fails on warn or error)
- fail_on_confidence: (empty — not checked)
- new_only: false
type FailResult ¶
type FailResult struct {
// Passed is true if no fail conditions were triggered.
Passed bool `json:"passed"`
// Reasons lists the reasons for failure, if any.
Reasons []string `json:"reasons,omitempty"`
// TriggeringFindings lists the findings that triggered failure.
TriggeringFindings []Finding `json:"triggering_findings,omitempty"`
}
FailResult holds the outcome of fail condition evaluation.
func Check ¶
func Check(result *InspectResult, policy FailPolicy) FailResult
Check evaluates the inspection result against the given fail policy and returns whether the check passes or fails.
This is the simplified API for CI quality gates. For baseline comparison, use EvaluateFailConditions instead.
func EvaluateFailConditions ¶
func EvaluateFailConditions(ir *InspectResult, policy FailPolicy, baseline *InspectResult) FailResult
EvaluateFailConditions evaluates the inspection result against the given fail policy and returns whether the check passes or fails.
A finding triggers failure if it matches ANY specified threshold:
- If FailOnSeverity is set and the finding's severity >= threshold, it fails.
- If FailOnConfidence is set and the finding's confidence >= threshold, it fails.
When policy.NewOnly is true and baseline is non-nil, only findings not present in the baseline are considered.
Example ¶
ExampleEvaluateFailConditions demonstrates CI fail condition evaluation.
package main
import (
"fmt"
"github.com/nao1215/mobilepkg"
)
func main() {
ar := &mobilepkg.InspectResult{
Findings: []mobilepkg.Finding{
{
ID: "manifest.debuggable",
Severity: mobilepkg.SeverityError,
Confidence: mobilepkg.ConfidenceHigh,
Message: "application is debuggable",
},
},
}
result := mobilepkg.EvaluateFailConditions(ar, mobilepkg.DefaultFailPolicy(), nil)
fmt.Println("Passed:", result.Passed)
fmt.Println("Reasons:", len(result.Reasons))
}
Output: Passed: false Reasons: 1
type Finding ¶
type Finding struct {
// ID is a unique, machine-readable identifier for this specific finding
// instance (e.g. "perm.dangerous.CAMERA", "secret.api_key.1").
ID string `json:"id"`
// Category groups related findings (e.g. "permission", "endpoint",
// "secret", "exported_component", "signing").
Category string `json:"category"`
// Severity classifies the importance of the finding.
Severity Severity `json:"severity"`
// Confidence indicates how certain the analysis is about this finding.
Confidence Confidence `json:"confidence"`
// Message is a human-readable description of the finding.
Message string `json:"message"`
// Evidence lists the supporting evidence for this finding.
Evidence []Evidence `json:"evidence"`
// Fingerprint is a stable identifier derived from the finding content,
// used for baseline comparison. Two findings with the same fingerprint
// across different runs are considered the same finding.
Fingerprint string `json:"fingerprint"`
}
Finding represents a security-relevant observation found during analysis. Each finding carries enough context for manual review (message, evidence) and for automated baseline comparison (id, fingerprint).
Findings are the primary output of security analysis. They are produced by the internal analysis step and included in [InspectResult.Findings].
func DiffFindings ¶
DiffFindings compares two slices of Finding and returns which were added and which were removed. The comparison key is ID + Fingerprint.
type Format ¶
type Format string
Format identifies the specific packaging format of a mobile application. While Platform tells you Android vs iOS, Format distinguishes the concrete archive layout (e.g. APK vs XAPK vs AAB).
const ( // FormatUnknown indicates an unrecognized format. FormatUnknown Format = "unknown" // FormatAPK indicates a standard Android APK. FormatAPK Format = "apk" // FormatIPA indicates an iOS IPA. FormatIPA Format = "ipa" // FormatXAPK indicates an XAPK (APKPure extended package with // manifest.json and one or more inner APKs). FormatXAPK Format = "xapk" // FormatAPKS indicates an APK Set archive produced by bundletool, // containing split APKs under a splits/ directory. FormatAPKS Format = "apks" // FormatAAB indicates an Android App Bundle whose manifest is // encoded in protobuf format. FormatAAB Format = "aab" )
type IconAsset ¶
type IconAsset struct {
// Path is the archive-internal path of the icon file.
Path string `json:"path"`
// Bytes contains the raw icon data.
// Excluded from JSON serialization to avoid bloating output.
Bytes []byte `json:"-"`
// Format describes the image format (e.g. "png", "jpeg").
Format string `json:"format"`
// Width is the icon width in pixels (0 if unknown).
Width int `json:"width"`
// Height is the icon height in pixels (0 if unknown).
Height int `json:"height"`
}
IconAsset holds the extracted icon data.
type Identity ¶
type Identity struct {
// Identifier is the package name (Android) or bundle ID (iOS).
Identifier string `json:"identifier"`
// DisplayName is the user-visible application name.
DisplayName string `json:"display_name"`
}
Identity holds the package identifier and display name.
type InspectError ¶
type InspectError struct {
// Code is a machine-readable error code such as "manifest.missing",
// "manifest.corrupt", "archive.corrupt", or "format.unsupported".
Code string
// Message is a human-readable description of the error.
Message string
// Err is the underlying cause, if any.
Err error
}
InspectError is a structured error returned by inspection functions. It carries a machine-readable [Code] for programmatic handling and supports errors.Is / errors.As through the wrapped [Err].
func (*InspectError) Error ¶
func (e *InspectError) Error() string
Error implements the error interface.
type InspectOptions ¶
type InspectOptions struct {
// Archive controls safety limits for archive processing.
// Nil means [DefaultArchiveLimits] are used.
Archive *ArchiveLimits
}
InspectOptions controls what InspectFileWithOptions extracts from a package. All sections are always extracted; the options control archive safety limits. Icons are always extracted at the best available size.
type InspectResult ¶
type InspectResult struct {
// Platform is the detected platform (android or ios).
Platform Platform `json:"platform"`
// Format identifies the specific packaging format (e.g. "apk", "ipa").
Format Format `json:"format"`
// Identity holds the package identifier and display name.
Identity Identity `json:"identity"`
// Version holds the marketing and build version strings.
Version Version `json:"version"`
// Entry holds the main entry point of the application.
Entry EntryPoint `json:"entry"`
// SDK holds the SDK and OS version constraints.
SDK SDKConstraints `json:"sdk"`
// Signing holds code signing and certificate information.
// Nil when signing information is not available.
Signing *SigningInfo `json:"signing,omitempty"`
// Icon holds the extracted icon asset, if available.
// Nil when no icon was found in the package.
Icon *IconAsset `json:"icon,omitempty"`
// Debuggable indicates whether the application is marked as debuggable.
Debuggable bool `json:"debuggable"`
// TestOnly indicates whether the application is marked as testOnly.
TestOnly bool `json:"test_only"`
// ProfileableByShell indicates whether the application can be profiled
// from the shell without root.
ProfileableByShell bool `json:"profileable_by_shell"`
// AllowBackup indicates whether the application allows data backup.
AllowBackup bool `json:"allow_backup"`
// UsesCleartextTraffic indicates whether the application allows
// cleartext (unencrypted HTTP) network traffic.
UsesCleartextTraffic bool `json:"uses_cleartext_traffic"`
// NetworkSecurityConfig is the resource reference to the network
// security configuration (Android only, e.g. "@xml/network_security_config").
// Empty when not declared or for iOS packages.
NetworkSecurityConfig string `json:"network_security_config,omitempty"`
// NSCPolicy holds the parsed network security configuration.
// Nil when no network_security_config.xml is found or for iOS packages.
NSCPolicy *NetworkSecurityPolicy `json:"nsc_policy,omitempty"`
// Permissions lists the declared permissions.
Permissions []Permission `json:"permissions"`
// ExportedComponents lists components that are exported and accessible
// to other applications.
ExportedComponents []ExportedComponent `json:"exported_components"`
// NetworkEndpoints lists network endpoints found in the package metadata.
NetworkEndpoints []NetworkEndpoint `json:"network_endpoints"`
// Findings holds security-relevant observations from analysis.
Findings []Finding `json:"findings"`
// SecretCandidates lists potential secrets, tokens, or credentials.
SecretCandidates []SecretCandidate `json:"secret_candidates"`
// Diff holds the comparison against a baseline, if provided.
Diff *Diff `json:"diff,omitempty"`
// Diagnostics collects non-fatal issues encountered during inspection
// and analysis.
Diagnostics []Diagnostic `json:"diagnostics"`
}
InspectResult is the single output of an inspection performed by InspectFile or Inspect. It combines package metadata, security-relevant facts, analysis findings, and diagnostics into one flat structure.
This is the primary result type for the mobilepkg API. It contains everything needed for CI quality gates, security review, and release comparison in a single value.
func Inspect ¶
Inspect performs a complete inspection from an io.ReaderAt. This is useful when the package is already in memory or comes from a non-file source (e.g. an HTTP response body).
The reader must contain a valid APK, XAPK, APKS, AAB, or IPA. It extracts metadata, runs security analysis, and returns a unified InspectResult.
func InspectFile ¶
func InspectFile(ctx context.Context, path string) (*InspectResult, error)
InspectFile performs a complete inspection of the mobile package at the given path. It extracts all available metadata, runs security analysis, and returns a unified InspectResult. No options are needed for the primary path — sensible defaults are applied automatically.
This is the primary entry point for the mobilepkg API. For expert use cases that need to control archive limits or icon size, use InspectFileWithOptions instead.
Example:
result, err := mobilepkg.InspectFile(ctx, "app.apk")
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Identity.Identifier)
for _, f := range result.Findings {
fmt.Printf("[%s] %s\n", f.Severity, f.Message)
}
Example ¶
ExampleInspectFile demonstrates the primary inspection path.
package main
import (
"context"
"fmt"
"github.com/nao1215/mobilepkg"
)
func main() {
// For demonstration, we show the API shape. In real usage, provide
// a valid APK/IPA path:
//
// result, err := mobilepkg.InspectFile(ctx, "app.apk")
// if err != nil {
// log.Fatal(err)
// }
// fmt.Println(result.Identity.Identifier)
// for _, f := range result.Findings {
// fmt.Printf("[%s] %s\n", f.Severity, f.Message)
// }
_ = context.Background()
// Construct a result manually for the example output.
result := &mobilepkg.InspectResult{
Platform: mobilepkg.PlatformAndroid,
Format: mobilepkg.FormatAPK,
Identity: mobilepkg.Identity{
Identifier: "com.example.helloworld",
DisplayName: "HelloWorld",
},
Version: mobilepkg.Version{Marketing: "1.0", Build: "1"},
Entry: mobilepkg.EntryPoint{Kind: "activity", Name: "com.example.helloworld.MainActivity"},
}
fmt.Println("Platform:", result.Platform)
fmt.Println("ID:", result.Identity.Identifier)
fmt.Println("Name:", result.Identity.DisplayName)
fmt.Println("Version:", result.Version.Marketing)
fmt.Println("Entry:", result.Entry.Kind, result.Entry.Name)
}
Output: Platform: android ID: com.example.helloworld Name: HelloWorld Version: 1.0 Entry: activity com.example.helloworld.MainActivity
func InspectFileWithOptions ¶
func InspectFileWithOptions(ctx context.Context, path string, opts InspectOptions) (*InspectResult, error)
InspectFileWithOptions performs a complete inspection of the mobile package at the given path with the specified options. It extracts all available metadata, runs security analysis, and returns a unified InspectResult.
Use this when you need to control archive safety limits. For most callers, InspectFile is sufficient.
func InspectWithBaseline ¶
func InspectWithBaseline(ctx context.Context, path string, baseline *InspectResult) (*InspectResult, error)
InspectWithBaseline performs a complete inspection and compares the result against a previous InspectResult. The baseline diff is included in the returned result.
This is useful for release-diff scenarios where you want to detect added/removed permissions, version bumps, or new security findings between two builds.
type IntentFilter ¶
type IntentFilter struct {
// Actions lists the intent actions (e.g. "android.intent.action.VIEW").
Actions []string `json:"actions,omitempty"`
// Categories lists the intent categories (e.g. "android.intent.category.DEFAULT").
Categories []string `json:"categories,omitempty"`
// Data lists the data specifications (scheme, host, path) for the filter.
Data []DataSpec `json:"data,omitempty"`
}
IntentFilter holds parsed intent-filter data from an Android manifest. Each filter declares what intents the component can respond to.
type NetworkEndpoint ¶
type NetworkEndpoint struct {
// Scheme is the URL scheme (e.g. "https", "http", "wss").
Scheme string `json:"scheme,omitempty"`
// Host is the hostname or IP address.
Host string `json:"host"`
// Port is the port number, if specified.
Port string `json:"port,omitempty"`
// Path is the URL path, if available.
Path string `json:"path,omitempty"`
// Source describes where this endpoint was found
// (e.g. "manifest", "info_plist", "entitlement").
Source string `json:"source"`
// Confidence indicates how certain the detection is.
Confidence Confidence `json:"confidence"`
}
NetworkEndpoint represents a network endpoint found in the package metadata.
type NetworkSecurityPolicy ¶
type NetworkSecurityPolicy struct {
// CleartextPermitted indicates whether the base config allows
// cleartext (HTTP) traffic.
CleartextPermitted bool `json:"cleartext_permitted"`
// DomainConfigs lists per-domain security configurations.
DomainConfigs []DomainConfig `json:"domain_configs,omitempty"`
// TrustAnchors lists the trust anchor sources (e.g. "system", "user",
// or a raw certificate reference).
TrustAnchors []string `json:"trust_anchors,omitempty"`
// HasPinSet is true if any domain config includes certificate pinning.
HasPinSet bool `json:"has_pin_set"`
// HasDebugOverrides is true if debug-overrides section is present.
HasDebugOverrides bool `json:"has_debug_overrides,omitempty"`
}
NetworkSecurityPolicy holds a partial summary of an Android network_security_config.xml file. It captures the base-config cleartext policy, top-level domain-configs (domains + cleartext + pin-set presence), and base-config trust-anchor sources.
Limitations: nested domain-configs, per-domain trust-anchors, and debug-overrides are not parsed. This provides a useful triage signal but is not a full representation of the config.
type Permission ¶
type Permission struct {
// Canonical is a cross-platform classification (e.g. "camera", "location").
// Empty if no mapping is available.
Canonical string `json:"canonical,omitempty"`
// RawName is the platform-specific permission identifier
// (e.g. "android.permission.CAMERA", "NSCameraUsageDescription").
RawName string `json:"raw_name"`
// Source indicates where the permission was declared
// ("manifest", "info_plist", or "entitlement").
Source string `json:"source"`
}
Permission represents a declared permission in a mobile application package.
type Platform ¶
type Platform string
Platform represents the mobile platform of an application package.
type ReportFile ¶
type ReportFile struct {
// SchemaVersion identifies the format version of this report file.
SchemaVersion string `json:"schema_version"`
// ToolVersion identifies the version of the tool that produced this report.
ToolVersion string `json:"tool_version"`
// Result contains the inspection output (metadata + findings + secrets + diff).
Result InspectResult `json:"result"`
// Verdict holds the fail-condition evaluation result, if a policy was
// applied. Nil when no policy was evaluated.
Verdict *FailResult `json:"verdict,omitempty"`
}
ReportFile is the top-level structure for the report.json output file. It wraps an InspectResult with metadata required for tooling. Use WriteReportJSON to serialize it.
func LoadReportFile ¶
func LoadReportFile(r io.Reader) (ReportFile, error)
LoadReportFile reads and parses a report.json file from the given reader. It validates that the schema_version matches SchemaVersion; mismatched versions produce a descriptive error so that stale baselines are caught early rather than causing subtle diff inaccuracies.
func NewReportFile ¶
func NewReportFile(ir *InspectResult, toolVersion string) ReportFile
NewReportFile creates a ReportFile from an InspectResult. Nil slices are normalized to empty slices so that JSON output contains [] instead of null.
Example ¶
ExampleNewReportFile demonstrates creating a report.json output.
package main
import (
"bytes"
"fmt"
"log"
"github.com/nao1215/mobilepkg"
)
func main() {
ar := &mobilepkg.InspectResult{
Platform: mobilepkg.PlatformAndroid,
Format: mobilepkg.FormatAPK,
Identity: mobilepkg.Identity{Identifier: "com.example.app"},
}
rf := mobilepkg.NewReportFile(ar, "1.0.0")
var buf bytes.Buffer
if err := mobilepkg.WriteReportJSON(&buf, rf); err != nil {
log.Fatal(err)
}
fmt.Println("Schema:", rf.SchemaVersion)
fmt.Println("Tool:", rf.ToolVersion)
}
Output: Schema: 1.0.0 Tool: 1.0.0
type Rule ¶
type Rule interface {
Validate(result *InspectResult) []Violation
}
Rule defines an inspection rule that checks an InspectResult for violations. Implementations should be stateless and safe for concurrent use.
func PermissionAllowList ¶
PermissionAllowList returns a Rule that reports a violation for every permission whose [Permission.RawName] is not in the allowed set.
func PermissionDenyList ¶
PermissionDenyList returns a Rule that reports a violation for every permission whose [Permission.RawName] is in the denied set.
func RequireFields ¶
RequireFields returns a Rule that checks whether the named fields are populated (non-empty) in the report. Supported field names:
- "identifier" — Identity.Identifier
- "display_name" — Identity.DisplayName
- "version_marketing" — Version.Marketing
- "version_build" — Version.Build
- "min_sdk" — SDK.MinSDK
- "target_sdk" — SDK.TargetSDK
- "entry_name" — Entry.Name
An error wrapping ErrUnknownField is returned if any field name is not recognized. This fail-closed behavior prevents silent misconfiguration.
func VersionFormat ¶
VersionFormat returns a Rule that checks whether the marketing version string matches the given regular expression pattern. An empty marketing version is not checked (use RequireFields for that).
type RuleFunc ¶
type RuleFunc func(*InspectResult) []Violation
RuleFunc is an adapter that allows ordinary functions to be used as [Rule]s.
type SDKConstraints ¶
type SDKConstraints struct {
// MinSDK is the minimum SDK/OS version required.
// Android: minSdkVersion (e.g. "21"). iOS: MinimumOSVersion (e.g. "15.0").
MinSDK string `json:"min_sdk,omitempty"`
// TargetSDK is the target SDK version (Android only).
// Empty for iOS packages.
TargetSDK string `json:"target_sdk,omitempty"`
}
SDKConstraints holds the SDK and OS version requirements extracted from the application manifest.
type SecretCandidate ¶
type SecretCandidate struct {
// Kind classifies the secret type (e.g. "api_key", "token", "aws_key").
Kind string `json:"kind"`
// MaskedValue shows a short prefix of the secret with the rest redacted.
MaskedValue string `json:"masked_value"`
// Source describes where this candidate was found.
Source string `json:"source"`
// Confidence indicates how certain the detection is.
Confidence Confidence `json:"confidence"`
}
SecretCandidate represents a potential secret, token, or credential found in the package. Raw values are never exposed; only a short masked prefix is provided for human identification.
type Severity ¶
type Severity string
Severity classifies the importance of a Diagnostic.
const ( // SeverityInfo indicates an informational note. SeverityInfo Severity = "info" // SeverityWarn indicates a potential problem that did not prevent extraction. SeverityWarn Severity = "warn" // SeverityError indicates a problem that prevented extraction of some data. SeverityError Severity = "error" )
type SigningInfo ¶
type SigningInfo struct {
// Scheme describes the signing schemes detected.
// Android examples: "v1", "v2", "v3", "v1+v2", "v1+v3".
// iOS: "apple".
Scheme string `json:"scheme"`
// Certificates lists the signing certificates found.
Certificates []CertSummary `json:"certificates,omitempty"`
// ProvisioningExpiresAt is the provisioning profile expiration date
// in RFC 3339 format (iOS only). Empty for Android or when unknown.
ProvisioningExpiresAt string `json:"provisioning_expires_at,omitempty"`
}
SigningInfo holds code signing and certificate information extracted from a mobile application package.
type Version ¶
type Version struct {
// Marketing is the user-facing version string
// (Android versionName / iOS CFBundleShortVersionString).
Marketing string `json:"marketing"`
// Build is the internal build identifier
// (Android versionCode / iOS CFBundleVersion).
Build string `json:"build"`
}
Version holds marketing and build version strings.
type Violation ¶
type Violation struct {
// RuleID is the machine-readable identifier of the rule that produced
// this violation (e.g. "required_field", "permission_denied").
RuleID string `json:"rule_id"`
// Severity classifies the importance of the violation.
Severity Severity `json:"severity"`
// Message is a human-readable description of the violation.
Message string `json:"message"`
// Field identifies the report field related to the violation
// (e.g. "Identity.Identifier", "Permissions[2].RawName").
Field string `json:"field"`
}
Violation represents a single rule violation found during validation.
func Validate ¶
func Validate(result *InspectResult, rules []Rule) []Violation
Validate applies every rule in rules to the result and collects all resulting violations into a single slice. Rules are evaluated in order.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
mobilepkg
command
mobilepkg is a CLI tool for inspecting mobile application packages.
|
mobilepkg is a CLI tool for inspecting mobile application packages. |
|
internal
|
|
|
cmdinfo
Package cmdinfo provides build metadata for the mobilepkg CLI.
|
Package cmdinfo provides build metadata for the mobilepkg CLI. |
|
dex
Package dex provides a minimal parser for Android DEX (Dalvik Executable) files.
|
Package dex provides a minimal parser for Android DEX (Dalvik Executable) files. |
|
platform/android
Package android provides Android-specific APK inspection logic.
|
Package android provides Android-specific APK inspection logic. |
|
platform/ios
Package ios provides iOS-specific IPA inspection logic.
|
Package ios provides iOS-specific IPA inspection logic. |
|
scanner
Package scanner provides a security rule engine for analyzing Android application packages using parsed DEX bytecode and manifest data.
|
Package scanner provides a security rule engine for analyzing Android application packages using parsed DEX bytecode and manifest data. |
|
secrets
Package secrets defines shared secret-detection patterns used by both the manifest/plist scanner (root package) and the DEX string scanner.
|
Package secrets defines shared secret-detection patterns used by both the manifest/plist scanner (root package) and the DEX string scanner. |
