Commit aec7b139 authored by Bella Bah's avatar Bella Bah Committed by Commit Bot

Rewrite Network Traffic Annotation Auditor from C++ to Python

tools/traffic_annotation/auditor/ is currently written in C++ which
poses some problems in the way of convenience. Rewriting it in
Python overcomes some of these issues.

Missing:
 - Exporter to annotations.xml.
 - Exporter to tsv.
 - Writing to a summary file.
 - Matching/completing partial annotations.
 - Handling exceptions list.
 - Performing all other remaining checks on annotations.
 - Raise an error on mutable tags.
 - Safelist filtering of extracted annotations.
 - Enforcing consistency between summary/grouping.xml and
   new annotations.
 - Checking for reserved ids and deprecated hash codes.
 - ID checking on annotations.
 - Handling removed annotations and deprecated annotations.
 - The remaining TODOs found in this CL.

Parity:
 - Extracting annotations according to the compiled proto.
 - Checking annotations' completeness.
 - Catching fatal/syntax errors.
 - "deserialization" of extracted annotations.

Bug: 1119417
Change-Id: Idd09b89577342e59011441b30a54437234ddb1bc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2364935
Commit-Queue: Mohamadou Bella Bah <bellabah@chromium.org>
Reviewed-by: default avatarRamin Halavati <rhalavati@chromium.org>
Reviewed-by: default avatarNicolas Ouellet-Payeur <nicolaso@chromium.org>
Cr-Commit-Position: refs/heads/master@{#802831}
parent b1470117
......@@ -15,6 +15,10 @@ script_dir = os.path.dirname(os.path.realpath(__file__))
tool_dir = os.path.abspath(os.path.join(script_dir, '../../clang/pylib'))
sys.path.insert(0, tool_dir)
#TODO(https://crbug.com/1119417): To be replaced with a commandline argument or
# made to run in addition to the preexisting C++ auditor executable.
PYTHON_AUDITOR = False
from clang import compile_db
class NetworkTrafficAnnotationTools():
......@@ -42,10 +46,15 @@ class NetworkTrafficAnnotationTools():
'darwin': 'mac',
'win32': 'win32',
}[sys.platform]
path = os.path.join(self.this_dir, '..', 'bin', platform,
if PYTHON_AUDITOR:
path = os.path.join(self.this_dir, "../scripts/auditor.py")
else:
path = os.path.join(self.this_dir, '..', 'bin', platform,
'traffic_annotation_auditor')
if sys.platform == 'win32':
path += '.exe'
if sys.platform == 'win32':
path += '.exe'
if os.path.exists(path):
self.auditor_path = path
......@@ -142,7 +151,12 @@ class NetworkTrafficAnnotationTools():
return_code: int Auditor's exit code.
"""
command_line = [self.auditor_path, "--build-path=" + self.build_path] + args
if PYTHON_AUDITOR:
command_line = [
"vpython", self.auditor_path, "--build-path=" + self.build_path] + args
else:
command_line = [self.auditor_path, "--build-path=" + self.build_path] + \
args
command = subprocess.Popen(
command_line, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
......
This diff is collapsed.
python_version: "2.7"
wheel: <
name: "infra/python/wheels/six-py2_py3"
version: "version:1.10.0"
>
wheel: <
name: "infra/python/wheels/protobuf-py2_py3"
version: "version:3.10.0"
>
wheel: <
name: "infra/python/wheels/enum34-py2"
version: "version:1.1.6"
>
wheel: <
name: "infra/python/wheels/aenum-py2_py3"
version: "version:2.1.2"
>
\ No newline at end of file
#!/usr/bin/env python
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Tests for the Traffic Annotation Auditor.
"""
import os
import sys
import argparse
import unittest
import auditor as auditor_py
from google.protobuf.json_format import ParseError
# Absolute path to chrome/src.
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
SRC_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, "../../.."))
TESTS_DIR = os.path.join(SCRIPT_DIR, "test_data")
# Set via the command line
build_path = ""
class TestTrafficAnnotationAuditor(unittest.TestCase):
def setUp(self):
unittest.TestCase.setUp(self)
path_filters = [os.path.relpath(
os.path.join(TESTS_DIR, "test_sample_annotations.cc"), SRC_DIR)]
all_annotations = auditor_py.TrafficAnnotationAuditor.run_extractor(
path_filters)
self.auditor_ui = auditor_py.TrafficAnnotationAuditorUI(
build_path, path_filters, False)
self.auditor = auditor_py.TrafficAnnotationAuditor()
self.sample_annotations = {}
for annotation in all_annotations:
self.sample_annotations[annotation.unique_id] = annotation
def test_setup(self):
"""|self.sample_annotations| should include all those inside
test_data/test_sample_annotations.cc"""
expected = [
"ok_annotation", "syntax_error_annotation", "incomplete_error_annotation"]
self.assertItemsEqual(expected, self.sample_annotations.keys())
def test_ensure_errors(self):
"""In the |test_sample_annotations.cc| there are some broken annotations.
This test ensures that TrafficAnnotationAuditorUI catches these errors by
running from start to finish via |.main()|
"""
# Suppress |self.auditor_ui.main()| prints to stdout.
sys.stdout = open(os.devnull, "w")
self.assertEqual(1, self.auditor_ui.main()) # 1 indicates errors caught.
sys.stdout = sys.__stdout__
def test_result_ok(self):
self.auditor.parse_extractor_output(
[self.sample_annotations["ok_annotation"]])
# Assert that correct annotation has been extracted and is OK (no errors).
self.assertTrue(self.auditor.extracted_annotations)
self.assertFalse(self.auditor.errors)
def test_syntax_error(self):
self.auditor.parse_extractor_output(
[self.sample_annotations["syntax_error_annotation"]])
self.assertTrue(self.auditor.errors)
result = self.auditor.errors[0]
self.assertEqual(auditor_py.AuditorError.Type.ERROR_SYNTAX, result.type)
self.assertTrue(
"sender: \"Cloud Policy\"': Expected \"{\"" in str(result))
def test_incomplete_error(self):
self.auditor.parse_extractor_output(
[self.sample_annotations["incomplete_error_annotation"]])
self.assertTrue(self.auditor.extracted_annotations)
self.auditor.run_all_checks()
self.assertTrue(self.auditor.errors)
result = self.auditor.errors[0]
self.assertEqual(
auditor_py.AuditorError.Type.ERROR_INCOMPLETE_ANNOTATION, result.type)
expected_missing_fields = [
"sender", "chrome_policy", "cookies_store",
"policy_exception_justification"]
missing_fields = str(result).split(
"missing fields:", 1)[1].lstrip().split(", ")
self.assertItemsEqual(expected_missing_fields, missing_fields)
if __name__ == "__main__":
args_parser = argparse.ArgumentParser(
description="Unittests for auditor.py")
args_parser.add_argument(
"--build-path",
help="Path to the build directory.",
default=os.path.join(SRC_DIR, "out/Default"))
args_parser.add_argument('unittest_args', nargs='*')
args = args_parser.parse_args()
build_path = args.build_path
sys.argv[1:] = args.unittest_args
unittest.main()
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// An OK annotation
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("ok_annotation", R"(
semantics {
sender: "Cloud Policy"
description:
"Used to fetch policy for extensions, policy-controlled wallpaper, "
"and custom terms of service."
trigger:
"Periodically loaded when a managed user is signed in to Chrome."
data:
"This request does not send any data. It loads external resources "
"by a unique URL provided by the admin."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: NO
setting:
"This feature cannot be controlled by Chrome settings, but users "
"can sign out of Chrome to disable it."
policy_exception_justification:
"Not implemented, considered not useful. This request is part of "
"the policy fetcher itself."
})");
// An annotation with a syntax error: semantics is missing '{'.
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("syntax_error_annotation", R"(
semantics
sender: "Cloud Policy"
description:
"Used to fetch policy for extensions, policy-controlled wallpaper, "
"and custom terms of service."
trigger:
"Periodically loaded when a managed user is signed in to Chrome."
data:
"This request does not send any data. It loads external resources "
"by a unique URL provided by the admin."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: NO
setting:
"This feature cannot be controlled by Chrome settings, but users "
"can sign out of Chrome to disable it."
policy_exception_justification:
"Not implemented, considered not useful. This request is part of "
"the policy fetcher itself."
})");
// An annotation with a completeness error: missing sender and
// policy_exception_justification, chrome_policy field.
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("incomplete_error_annotation", R"(
semantics {
description:
"Used to fetch policy for extensions, policy-controlled wallpaper, "
"and custom terms of service."
trigger:
"Periodically loaded when a managed user is signed in to Chrome."
data:
"This request does not send any data. It loads external resources "
"by a unique URL provided by the admin."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: YES
setting:
"This feature cannot be controlled by Chrome settings, but users "
"can sign out of Chrome to disable it."
})");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment