Skip to content

Commit

Permalink
Merge 9149315 into e569a68
Browse files Browse the repository at this point in the history
  • Loading branch information
pmarkowsky authored Sep 22, 2021
2 parents e569a68 + 9149315 commit a6c8517
Show file tree
Hide file tree
Showing 18 changed files with 1,160 additions and 1 deletion.
53 changes: 53 additions & 0 deletions Source/santametricservice/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_command_line_application")
load("//:helper.bzl", "santa_unit_test")

package(default_visibility = ["//:santa_package_group"])

licenses(["notice"]) # Apache 2.0

objc_library(
name = "SNTMetricServiceLib",
srcs = [
"SNTMetricService.h",
"SNTMetricService.m",
"main.m",
],
deps = [
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
"//Source/common:SNTMetricSet",
"//Source/common:SNTXPCMetricServiceInterface",
"//Source/santametricservice/Formats:SNTMetricRawJsonFormat",
"//Source/santametricservice/Writers:SNTMetricFileWriter",
"@MOLCodesignChecker",
"@MOLXPCConnection",
],
)

santa_unit_test(
name = "SNTMetricServiceTest",
srcs = ["SNTMetricServiceTest.m"],
deps = [
":SNTMetricServiceLib",
"@OCMock",
],
)

test_suite(
name = "unit_tests",
tests = [
":SNTMetricServiceTest",
"//Source/santametricservice/Formats:SNTMetricRawJsonFormatTest",
"//Source/santametricservice/Writers:SNTMetricFileWriterTest",
],
)

macos_command_line_application(
name = "santametricservice",
bundle_id = "com.google.santa.metricservice",
infoplists = ["Info.plist"],
minimum_os_version = "10.15",
version = "//:version",
visibility = ["//:santa_package_group"],
deps = [":SNTMetricServiceLib"],
)
43 changes: 43 additions & 0 deletions Source/santametricservice/Formats/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_command_line_application")
load("//:helper.bzl", "santa_unit_test")

package(default_visibility = ["//:santa_package_group"])

licenses(["notice"]) # Apache 2.0

objc_library(
name = "SNTMetricFormat",
hdrs = ["SNTMetricFormat.h"],
)

objc_library(
name = "SNTMetricRawJsonFormat",
srcs = [
"SNTMetricFormat.h",
"SNTMetricRawJsonFormat.h",
"SNTMetricRawJsonFormat.m",
],
deps = [
":SNTMetricFormat",
"//Source/common:SNTLogging",
],
)

santa_unit_test(
name = "SNTMetricRawJsonFormatTest",
srcs = [
"SNTMetricRawJsonFormatTest.m",
],
structured_resources = glob(["testdata/**"]),
deps = [
":SNTMetricRawJsonFormat",
"//Source/common:SNTMetricSet",
],
)

test_suite(
name = "format_tests",
tests = [
":SNTMetricRawJsonFormatTest",
],
)
19 changes: 19 additions & 0 deletions Source/santametricservice/Formats/SNTMetricFormat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/// Copyright 2021 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.

#import <Foundation/Foundation.h>

@protocol SNTMetricFormat
- (NSArray<NSData *> *)convert:(NSDictionary *)metrics error:(NSError **)err;
@end
21 changes: 21 additions & 0 deletions Source/santametricservice/Formats/SNTMetricRawJSONFormat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/// Copyright 2021 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.

#import <Foundation/Foundation.h>

#import "Source/santametricservice/Formats/SNTMetricFormat.h"

@interface SNTMetricRawJSONFormat : NSObject <SNTMetricFormat>
- (NSArray<NSData *> *)convert:(NSDictionary *)metrics error:(NSError **)err;
@end
94 changes: 94 additions & 0 deletions Source/santametricservice/Formats/SNTMetricRawJSONFormat.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/// Copyright 2021 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/common/SNTLogging.h"

#import "Source/santametricservice/Formats/SNTMetricRawJSONFormat.h"

@implementation SNTMetricRawJSONFormat {
NSDateFormatter *dateFormatter;
}

- (instancetype)init {
self = [super init];
if (self) {
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
}
return self;
}

- (NSArray *)normalizeArray:(NSArray *)arr {
NSMutableArray *normalized = [NSMutableArray arrayWithArray:arr];

for (int i = 0; i < [arr count]; i++) {
if ([arr[i] isKindOfClass:[NSArray class]]) {
normalized[i] = [self normalizeArray:(NSArray *)arr[i]];
} else if ([arr[i] isKindOfClass:[NSDictionary class]]) {
normalized[i] = [self normalize:(NSDictionary *)arr[i]];
}
}

return normalized;
}

/**
* Normalizes the metrics dictionary for exporting to JSON
**/
- (NSDictionary *)normalize:(NSDictionary *)metrics {
// Convert NSDate's to RFC3339 in strings as NSDate's cannot be serialized
// to JSON.
NSMutableDictionary *normalizedMetrics = [NSMutableDictionary dictionaryWithDictionary:metrics];

for (NSString *key in metrics) {
const id object = [metrics objectForKey:key];
if ([object isKindOfClass:[NSDate class]]) {
normalizedMetrics[key] = [self->dateFormatter stringFromDate:(NSDate *)object];
} else if ([object isKindOfClass:[NSDictionary class]]) {
normalizedMetrics[key] = [self normalize:metrics[key]];
} else if ([object isKindOfClass:[NSArray class]]) {
normalizedMetrics[key] = [self normalizeArray:(NSArray *)object];
}
}

return (NSDictionary *)normalizedMetrics;
}

/*
* Convert normalies and converts the metrics dictionary to a single JSON
* object.
*
* @param metrics an NSDictionary exported by the SNTMetricSet
* @param error a pointer to an NSError to allow errors to bubble up.
*
* Returns an NSArray containing one entry of all metrics serialized to JSON or
* nil on error.
*/
- (NSArray<NSData *> *)convert:(NSDictionary *)metrics error:(NSError **)err {
NSDictionary *normalizedMetrics = [self normalize:metrics];

if (![NSJSONSerialization isValidJSONObject:normalizedMetrics]) {
LOGE(@"unable to convert metrics to JSON: invalid metrics");
return nil;
}

NSData *json = [NSJSONSerialization dataWithJSONObject:normalizedMetrics
options:NSJSONWritingPrettyPrinted
error:err];
if (json == nil && *err != nil) {
return nil;
}

return @[ json ];
}
@end
153 changes: 153 additions & 0 deletions Source/santametricservice/Formats/SNTMetricRawJSONFormatTest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#import <XCTest/XCTest.h>

#import "Source/common/SNTMetricSet.h"
#import "Source/santametricservice/Formats/SNTMetricRawJSONFormat.h"

NSDictionary *validMetricsDict = nil;

@interface SNTMetricRawJSONFormatTest : XCTestCase
@end

@implementation SNTMetricRawJSONFormatTest

- (void)initializeValidMetricsDict {
NSDateFormatter *formatter = NSDateFormatter.new;
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
NSDate *fixedDate = [formatter dateFromString:@"2021-09-16T21:07:34.826Z"];

validMetricsDict = @{
@"root_labels" : @{@"hostname" : @"testHost", @"username" : @"testUser"},
@"metrics" : @{
@"/build/label" : @{
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeConstantString],
@"fields" : @{
@"" : @[ @{
@"value" : @"",
@"created" : fixedDate,
@"last_updated" : fixedDate,
@"data" : @"20210809.0.1"
} ]
}
},
@"/santa/events" : @{
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeCounter],
@"fields" : @{
@"rule_type" : @[
@{
@"value" : @"binary",
@"created" : fixedDate,
@"last_updated" : fixedDate,
@"data" : @1,
},
@{
@"value" : @"certificate",
@"created" : fixedDate,
@"last_updated" : fixedDate,
@"data" : @2,
},
],
},
},
@"/santa/rules" : @{
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeInt64],
@"fields" : @{
@"rule_type" : @[
@{
@"value" : @"binary",
@"created" : fixedDate,
@"last_updated" : fixedDate,
@"data" : @1
},
@{
@"value" : @"certificate",
@"created" : fixedDate,
@"last_updated" : fixedDate,
@"data" : @3
}
]
},
},
@"/santa/using_endpoint_security_framework" : @{
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeConstantBool],
@"fields" : @{
@"" : @[ @{
@"value" : @"",
@"created" : fixedDate,
@"last_updated" : fixedDate,
@"data" : [NSNumber numberWithBool:YES]
} ]
}
},
@"/proc/birth_timestamp" : @{
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeConstantInt64],
@"fields" : @{
@"" : @[ @{
@"value" : @"",
@"created" : fixedDate,
@"last_updated" : fixedDate,
@"data" : [NSNumber numberWithLong:1250999830800]
} ]
},
},
@"/proc/memory/virtual_size" : @{
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeInt64],
@"fields" : @{
@"" : @[ @{
@"value" : @"",
@"created" : fixedDate,
@"last_updated" : fixedDate,
@"data" : [NSNumber numberWithInt:987654321]
} ]
}
},
@"/proc/memory/resident_size" : @{
@"type" : [NSNumber numberWithInt:(int)SNTMetricTypeGaugeInt64],
@"fields" : @{
@"" : @[ @{
@"value" : @"",
@"created" : fixedDate,
@"last_updated" : fixedDate,
@"data" : [NSNumber numberWithInt:123456789]
} ]
},
},
}
};
}

- (void)setUp {
[self initializeValidMetricsDict];
}

- (void)testMetricsConversionToJSON {
SNTMetricRawJSONFormat *formatter = [[SNTMetricRawJSONFormat alloc] init];
NSError *err = nil;
NSArray<NSData *> *output = [formatter convert:validMetricsDict error:&err];

XCTAssertEqual(1, [output count]);
XCTAssertNotNil(output[0]);
XCTAssertNil(err);

NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:output[0]
options:NSJSONReadingAllowFragments
error:&err];
XCTAssertNotNil(jsonDict);

NSString *path = [[NSBundle bundleForClass:[self class]] resourcePath];
path = [path stringByAppendingPathComponent:@"testdata/json/test.json"];

NSFileManager *filemgr = [NSFileManager defaultManager];
NSData *goldenFileData = [filemgr contentsAtPath:path];

XCTAssertNotNil(goldenFileData, @"unable to open / read golden file");

NSDictionary *expectedJSONDict =
[NSJSONSerialization JSONObjectWithData:goldenFileData
options:NSJSONReadingAllowFragments
error:&err];

XCTAssertNotNil(expectedJSONDict);
XCTAssertEqualObjects(expectedJSONDict, jsonDict, @"generated JSON does not match golden file.");
}

@end
Loading

0 comments on commit a6c8517

Please sign in to comment.