Skip to content

Commit

Permalink
feat: initial parsing of security metrics (#33)
Browse files Browse the repository at this point in the history
* feat: initial parsing of security metrics

* fix: parse array/slice of security metrics

* fix: perform basic validation on SecurityMetrics

* fix: error messages

* fix: add test cases for security metrics validation

* fix: added Name field to SecurityMetric type

* fix: typo in securityMetricMissingNameError
  • Loading branch information
Dara Hayes committed Mar 5, 2018
1 parent 38a27f1 commit 69253c6
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 11 deletions.
49 changes: 44 additions & 5 deletions pkg/mobile/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ type Metric struct {
}

type MetricData struct {
App *AppMetric `json:"app,omitempty"`
Device *DeviceMetric `json:"device,omitempty"`
App *AppMetric `json:"app,omitempty"`
Device *DeviceMetric `json:"device,omitempty"`
Security *SecurityMetrics `json:"security,omitempty"`
}

type AppMetric struct {
Expand All @@ -33,13 +34,31 @@ type DeviceMetric struct {
PlatformVersion string `json:"platformVersion"`
}

type SecurityMetrics []SecurityMetric

type SecurityMetric struct {
Type *string `json:"type,omitempty"`
Name *string `json:"name,omitempty"`
Passed *bool `json:"passed,omitempty"`
}

const clientIdMaxLength = 128
const securityMetricsMaxLength = 30

const missingClientIdError = "missing clientId in payload"
const invalidTimestampError = "timestamp must be a valid number"
const missingDataError = "missing metrics data in payload"
const securityMetricsEmptyError = "data.security cannot be empty"
const securityMetricMissingTypeError = "invalid element in data.security at position %v, type must be included"
const securityMetricMissingNameError = "invalid element in data.security at position %v, name must be included"
const securityMetricMissingPassedError = "invalid element in data.security at position %v, passed must be included"

var clientIdLengthError = fmt.Sprintf("clientId exceeded maximum length of %v", clientIdMaxLength)
var securityMetricsLengthError = fmt.Sprintf("maximum length of data.security %v", securityMetricsMaxLength)

func (m *Metric) Validate() (valid bool, reason string) {
if m.ClientId == "" {
return false, "missing clientId in payload"
return false, missingClientIdError
}

if len(m.ClientId) > clientIdMaxLength {
Expand All @@ -48,13 +67,33 @@ func (m *Metric) Validate() (valid bool, reason string) {

if m.ClientTimestamp != "" {
if _, err := m.ClientTimestamp.Int64(); err != nil {
return false, "timestamp must be a valid number"
return false, invalidTimestampError
}
}

// check if data field was missing or empty object
if m.Data == nil || (MetricData{}) == *m.Data {
return false, "missing metrics data in payload"
return false, missingDataError
}

if m.Data.Security != nil {
if len(*m.Data.Security) == 0 {
return false, securityMetricsEmptyError
}
if len(*m.Data.Security) > securityMetricsMaxLength {
return false, securityMetricsLengthError
}
for i, sm := range *m.Data.Security {
if sm.Type == nil {
return false, fmt.Sprintf(securityMetricMissingTypeError, i)
}
if sm.Name == nil {
return false, fmt.Sprintf(securityMetricMissingNameError, i)
}
if sm.Passed == nil {
return false, fmt.Sprintf(securityMetricMissingPassedError, i)
}
}
}
return true, ""
}
51 changes: 45 additions & 6 deletions pkg/mobile/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ import (

func TestMetricValidate(t *testing.T) {

securityMetricType := "org.aerogear.mobile.security.checks.TestCheck"
securityMetricName := "TestCheck"
securityMetricPassed := true

bigSecurityMetricList := SecurityMetrics{}
for i := 0; i <= clientIdMaxLength+1; i++ {
bigSecurityMetricList = append(bigSecurityMetricList, SecurityMetric{Type: &securityMetricType, Passed: &securityMetricPassed})
}

testCases := []struct {
Name string
Metric Metric
Expand All @@ -18,31 +27,31 @@ func TestMetricValidate(t *testing.T) {
Name: "Empty metric should be invalid",
Metric: Metric{},
Valid: false,
ExpectedReason: "missing clientId in payload",
ExpectedReason: missingClientIdError,
},
{
Name: "Metric with no clientId should be invalid",
Metric: Metric{Data: &MetricData{App: &AppMetric{SDKVersion: "1"}}},
Valid: false,
ExpectedReason: "missing clientId in payload",
ExpectedReason: missingClientIdError,
},
{
Name: "Metric with long clientId should be invalid",
Metric: Metric{ClientId: strings.Join(make([]string, clientIdMaxLength+10), "a"), Data: &MetricData{App: &AppMetric{SDKVersion: "1"}}},
Valid: false,
ExpectedReason: fmt.Sprintf("clientId exceeded maximum length of %v", clientIdMaxLength),
ExpectedReason: clientIdLengthError,
},
{
Name: "Metric with no Data should be invalid",
Metric: Metric{ClientId: "org.aerogear.metrics.testing"},
Valid: false,
ExpectedReason: "missing metrics data in payload",
ExpectedReason: missingDataError,
},
{
Name: "Metric with empty Data should be invalid",
Metric: Metric{ClientId: "org.aerogear.metrics.testing", Data: &MetricData{}},
Valid: false,
ExpectedReason: "missing metrics data in payload",
ExpectedReason: missingDataError,
},
{
Name: "Metric with ClientId and Some Data should be valid",
Expand All @@ -54,14 +63,44 @@ func TestMetricValidate(t *testing.T) {
Name: "Metric with bad timestamp should be invalid",
Metric: Metric{ClientId: "org.aerogear.metrics.testing", ClientTimestamp: "invalid", Data: &MetricData{App: &AppMetric{SDKVersion: "1"}}},
Valid: false,
ExpectedReason: "timestamp must be a valid number",
ExpectedReason: invalidTimestampError,
},
{
Name: "Metric with valid timestamp should be valid",
Metric: Metric{ClientId: "org.aerogear.metrics.testing", ClientTimestamp: "12345", Data: &MetricData{App: &AppMetric{SDKVersion: "1"}}},
Valid: true,
ExpectedReason: "",
},
{
Name: "Security Metrics with missing type field should be invalid",
Metric: Metric{ClientId: "org.aerogear.metrics.testing", Data: &MetricData{Security: &SecurityMetrics{SecurityMetric{Type: nil, Name: &securityMetricName, Passed: &securityMetricPassed}}}},
Valid: false,
ExpectedReason: fmt.Sprintf(securityMetricMissingTypeError, 0),
},
{
Name: "Security Metrics with missing name field should be invalid",
Metric: Metric{ClientId: "org.aerogear.metrics.testing", Data: &MetricData{Security: &SecurityMetrics{SecurityMetric{Type: &securityMetricType, Name: nil, Passed: &securityMetricPassed}}}},
Valid: false,
ExpectedReason: fmt.Sprintf(securityMetricMissingNameError, 0),
},
{
Name: "Security Metrics with missing passed field should be invalid",
Metric: Metric{ClientId: "org.aerogear.metrics.testing", Data: &MetricData{Security: &SecurityMetrics{SecurityMetric{Type: &securityMetricType, Name: &securityMetricName, Passed: nil}}}},
Valid: false,
ExpectedReason: fmt.Sprintf(securityMetricMissingPassedError, 0),
},
{
Name: "Empty Security Metrics slice should be invalid",
Metric: Metric{ClientId: "org.aerogear.metrics.testing", Data: &MetricData{Security: &SecurityMetrics{}}},
Valid: false,
ExpectedReason: securityMetricsEmptyError,
},
{
Name: "Security Metrics slice with length > max length should be valid",
Metric: Metric{ClientId: "org.aerogear.metrics.testing", Data: &MetricData{Security: &bigSecurityMetricList}},
Valid: false,
ExpectedReason: securityMetricsLengthError,
},
}

for _, tc := range testCases {
Expand Down

0 comments on commit 69253c6

Please sign in to comment.