Skip to content

Commit

Permalink
Merge 1221437 into e569a68
Browse files Browse the repository at this point in the history
  • Loading branch information
pmarkowsky committed Sep 22, 2021
2 parents e569a68 + 1221437 commit 4a3e660
Show file tree
Hide file tree
Showing 18 changed files with 1,188 additions and 1 deletion.
54 changes: 54 additions & 0 deletions Source/santametricservice/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
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:SNTXPCMetricServiceInterface",
"//Source/common:SNTLogging",
"//Source/common:SNTConfigurator",
"//Source/common:SNTMetricSet",
"//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 = [
"SNTMetricRawJsonFormat.h",
"SNTMetricRawJsonFormat.m",
"SNTMetricFormat.h",
],
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
98 changes: 98 additions & 0 deletions Source/santametricservice/Formats/SNTMetricRawJsonFormat.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/// 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
155 changes: 155 additions & 0 deletions Source/santametricservice/Formats/SNTMetricRawJsonFormatTest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#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

0 comments on commit 4a3e660

Please sign in to comment.