summaryrefslogtreecommitdiffstats
path: root/chromium/components/policy/tools/syntax_check_policy_template_json.py
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/policy/tools/syntax_check_policy_template_json.py')
-rwxr-xr-xchromium/components/policy/tools/syntax_check_policy_template_json.py501
1 files changed, 0 insertions, 501 deletions
diff --git a/chromium/components/policy/tools/syntax_check_policy_template_json.py b/chromium/components/policy/tools/syntax_check_policy_template_json.py
deleted file mode 100755
index f51e3e3dc5a..00000000000
--- a/chromium/components/policy/tools/syntax_check_policy_template_json.py
+++ /dev/null
@@ -1,501 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012 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.
-
-'''
-Checks a policy_templates.json file for conformity to its syntax specification.
-'''
-
-import json
-import optparse
-import os
-import re
-import sys
-
-
-LEADING_WHITESPACE = re.compile('^([ \t]*)')
-TRAILING_WHITESPACE = re.compile('.*?([ \t]+)$')
-# Matches all non-empty strings that contain no whitespaces.
-NO_WHITESPACE = re.compile('[^\s]+$')
-
-# Convert a 'type' to its corresponding schema type.
-TYPE_TO_SCHEMA = {
- 'int': 'integer',
- 'list': 'array',
- 'dict': 'object',
- 'main': 'boolean',
- 'string': 'string',
- 'int-enum': 'integer',
- 'string-enum': 'string',
- 'external': 'object',
-}
-
-# List of boolean policies that have been introduced with negative polarity in
-# the past and should not trigger the negative polarity check.
-LEGACY_INVERTED_POLARITY_WHITELIST = [
- 'DeveloperToolsDisabled',
- 'DeviceAutoUpdateDisabled',
- 'Disable3DAPIs',
- 'DisableAuthNegotiateCnameLookup',
- 'DisablePluginFinder',
- 'DisablePrintPreview',
- 'DisableSafeBrowsingProceedAnyway',
- 'DisableScreenshots',
- 'DisableSpdy',
- 'DisableSSLRecordSplitting',
- 'DriveDisabled',
- 'DriveDisabledOverCellular',
- 'ExternalStorageDisabled',
- 'SavingBrowserHistoryDisabled',
- 'SyncDisabled',
-]
-
-class PolicyTemplateChecker(object):
-
- def __init__(self):
- self.error_count = 0
- self.warning_count = 0
- self.num_policies = 0
- self.num_groups = 0
- self.num_policies_in_groups = 0
- self.options = None
- self.features = []
-
- def _Error(self, message, parent_element=None, identifier=None,
- offending_snippet=None):
- self.error_count += 1
- error = ''
- if identifier is not None and parent_element is not None:
- error += 'In %s %s: ' % (parent_element, identifier)
- print error + 'Error: ' + message
- if offending_snippet is not None:
- print ' Offending:', json.dumps(offending_snippet, indent=2)
-
- def _CheckContains(self, container, key, value_type,
- optional=False,
- parent_element='policy',
- container_name=None,
- identifier=None,
- offending='__CONTAINER__',
- regexp_check=None):
- '''
- Checks |container| for presence of |key| with value of type |value_type|.
- If |value_type| is string and |regexp_check| is specified, then an error is
- reported when the value does not match the regular expression object.
-
- The other parameters are needed to generate, if applicable, an appropriate
- human-readable error message of the following form:
-
- In |parent_element| |identifier|:
- (if the key is not present):
- Error: |container_name| must have a |value_type| named |key|.
- Offending snippet: |offending| (if specified; defaults to |container|)
- (if the value does not have the required type):
- Error: Value of |key| must be a |value_type|.
- Offending snippet: |container[key]|
-
- Returns: |container[key]| if the key is present, None otherwise.
- '''
- if identifier is None:
- identifier = container.get('name')
- if container_name is None:
- container_name = parent_element
- if offending == '__CONTAINER__':
- offending = container
- if key not in container:
- if optional:
- return
- else:
- self._Error('%s must have a %s "%s".' %
- (container_name.title(), value_type.__name__, key),
- container_name, identifier, offending)
- return None
- value = container[key]
- if not isinstance(value, value_type):
- self._Error('Value of "%s" must be a %s.' %
- (key, value_type.__name__),
- container_name, identifier, value)
- if value_type == str and regexp_check and not regexp_check.match(value):
- self._Error('Value of "%s" must match "%s".' %
- (key, regexp_check.pattern),
- container_name, identifier, value)
- return value
-
- def _AddPolicyID(self, id, policy_ids, policy):
- '''
- Adds |id| to |policy_ids|. Generates an error message if the
- |id| exists already; |policy| is needed for this message.
- '''
- if id in policy_ids:
- self._Error('Duplicate id', 'policy', policy.get('name'),
- id)
- else:
- policy_ids.add(id)
-
- def _CheckPolicyIDs(self, policy_ids):
- '''
- Checks a set of policy_ids to make sure it contains a continuous range
- of entries (i.e. no holes).
- Holes would not be a technical problem, but we want to ensure that nobody
- accidentally omits IDs.
- '''
- for i in range(len(policy_ids)):
- if (i + 1) not in policy_ids:
- self._Error('No policy with id: %s' % (i + 1))
-
- def _CheckPolicySchema(self, policy, policy_type):
- '''Checks that the 'schema' field matches the 'type' field.'''
- self._CheckContains(policy, 'schema', dict)
- if isinstance(policy.get('schema'), dict):
- self._CheckContains(policy['schema'], 'type', str)
- schema_type = policy['schema'].get('type')
- if schema_type != TYPE_TO_SCHEMA[policy_type]:
- self._Error('Schema type must match the existing type for policy %s' %
- policy.get('name'))
-
- # Checks that boolean policies are not negated (which makes them harder to
- # reason about).
- if (schema_type == 'boolean' and
- 'disable' in policy.get('name').lower() and
- policy.get('name') not in LEGACY_INVERTED_POLARITY_WHITELIST):
- self._Error(('Boolean policy %s uses negative polarity, please make ' +
- 'new boolean policies follow the XYZEnabled pattern. ' +
- 'See also http://crbug.com/85687') % policy.get('name'))
-
-
- def _CheckPolicy(self, policy, is_in_group, policy_ids):
- if not isinstance(policy, dict):
- self._Error('Each policy must be a dictionary.', 'policy', None, policy)
- return
-
- # There should not be any unknown keys in |policy|.
- for key in policy:
- if key not in ('name', 'type', 'caption', 'desc', 'device_only',
- 'supported_on', 'label', 'policies', 'items',
- 'example_value', 'features', 'deprecated', 'future',
- 'id', 'schema', 'max_size'):
- self.warning_count += 1
- print ('In policy %s: Warning: Unknown key: %s' %
- (policy.get('name'), key))
-
- # Each policy must have a name.
- self._CheckContains(policy, 'name', str, regexp_check=NO_WHITESPACE)
-
- # Each policy must have a type.
- policy_types = ('group', 'main', 'string', 'int', 'list', 'int-enum',
- 'string-enum', 'dict', 'external')
- policy_type = self._CheckContains(policy, 'type', str)
- if policy_type not in policy_types:
- self._Error('Policy type must be one of: ' + ', '.join(policy_types),
- 'policy', policy.get('name'), policy_type)
- return # Can't continue for unsupported type.
-
- # Each policy must have a caption message.
- self._CheckContains(policy, 'caption', str)
-
- # Each policy must have a description message.
- self._CheckContains(policy, 'desc', str)
-
- # If 'label' is present, it must be a string.
- self._CheckContains(policy, 'label', str, True)
-
- # If 'deprecated' is present, it must be a bool.
- self._CheckContains(policy, 'deprecated', bool, True)
-
- # If 'future' is present, it must be a bool.
- self._CheckContains(policy, 'future', bool, True)
-
- if policy_type == 'group':
- # Groups must not be nested.
- if is_in_group:
- self._Error('Policy groups must not be nested.', 'policy', policy)
-
- # Each policy group must have a list of policies.
- policies = self._CheckContains(policy, 'policies', list)
-
- # Check sub-policies.
- if policies is not None:
- for nested_policy in policies:
- self._CheckPolicy(nested_policy, True, policy_ids)
-
- # Groups must not have an |id|.
- if 'id' in policy:
- self._Error('Policies of type "group" must not have an "id" field.',
- 'policy', policy)
-
- # Statistics.
- self.num_groups += 1
-
- else: # policy_type != group
- # Each policy must have a protobuf ID.
- id = self._CheckContains(policy, 'id', int)
- self._AddPolicyID(id, policy_ids, policy)
-
- # 'schema' is the new 'type'.
- # TODO(joaodasilva): remove the 'type' checks once 'schema' is used
- # everywhere.
- self._CheckPolicySchema(policy, policy_type)
-
- # Each policy must have a supported_on list.
- supported_on = self._CheckContains(policy, 'supported_on', list)
- if supported_on is not None:
- for s in supported_on:
- if not isinstance(s, str):
- self._Error('Entries in "supported_on" must be strings.', 'policy',
- policy, supported_on)
-
- # Each policy must have a 'features' dict.
- features = self._CheckContains(policy, 'features', dict)
-
- # All the features must have a documenting message.
- if features:
- for feature in features:
- if not feature in self.features:
- self._Error('Unknown feature "%s". Known features must have a '
- 'documentation string in the messages dictionary.' %
- feature, 'policy', policy.get('name', policy))
-
- # All user policies must have a per_profile feature flag.
- if (not policy.get('device_only', False) and
- not policy.get('deprecated', False) and
- not filter(re.compile('^chrome_frame:.*').match, supported_on)):
- self._CheckContains(features, 'per_profile', bool,
- container_name='features',
- identifier=policy.get('name'))
-
- # All policies must declare whether they allow changes at runtime.
- self._CheckContains(features, 'dynamic_refresh', bool,
- container_name='features',
- identifier=policy.get('name'))
-
- # Each policy must have an 'example_value' of appropriate type.
- if policy_type == 'main':
- value_type = bool
- elif policy_type in ('string', 'string-enum'):
- value_type = str
- elif policy_type in ('int', 'int-enum'):
- value_type = int
- elif policy_type == 'list':
- value_type = list
- elif policy_type in ('dict', 'external'):
- value_type = dict
- else:
- raise NotImplementedError('Unimplemented policy type: %s' % policy_type)
- self._CheckContains(policy, 'example_value', value_type)
-
- # Statistics.
- self.num_policies += 1
- if is_in_group:
- self.num_policies_in_groups += 1
-
- if policy_type in ('int-enum', 'string-enum'):
- # Enums must contain a list of items.
- items = self._CheckContains(policy, 'items', list)
- if items is not None:
- if len(items) < 1:
- self._Error('"items" must not be empty.', 'policy', policy, items)
- for item in items:
- # Each item must have a name.
- # Note: |policy.get('name')| is used instead of |policy['name']|
- # because it returns None rather than failing when no key called
- # 'name' exists.
- self._CheckContains(item, 'name', str, container_name='item',
- identifier=policy.get('name'),
- regexp_check=NO_WHITESPACE)
-
- # Each item must have a value of the correct type.
- self._CheckContains(item, 'value', value_type, container_name='item',
- identifier=policy.get('name'))
-
- # Each item must have a caption.
- self._CheckContains(item, 'caption', str, container_name='item',
- identifier=policy.get('name'))
-
- if policy_type == 'external':
- # Each policy referencing external data must specify a maximum data size.
- self._CheckContains(policy, 'max_size', int)
-
- def _CheckMessage(self, key, value):
- # |key| must be a string, |value| a dict.
- if not isinstance(key, str):
- self._Error('Each message key must be a string.', 'message', key, key)
- return
-
- if not isinstance(value, dict):
- self._Error('Each message must be a dictionary.', 'message', key, value)
- return
-
- # Each message must have a desc.
- self._CheckContains(value, 'desc', str, parent_element='message',
- identifier=key)
-
- # Each message must have a text.
- self._CheckContains(value, 'text', str, parent_element='message',
- identifier=key)
-
- # There should not be any unknown keys in |value|.
- for vkey in value:
- if vkey not in ('desc', 'text'):
- self.warning_count += 1
- print 'In message %s: Warning: Unknown key: %s' % (key, vkey)
-
- def _LeadingWhitespace(self, line):
- match = LEADING_WHITESPACE.match(line)
- if match:
- return match.group(1)
- return ''
-
- def _TrailingWhitespace(self, line):
- match = TRAILING_WHITESPACE.match(line)
- if match:
- return match.group(1)
- return ''
-
- def _LineError(self, message, line_number):
- self.error_count += 1
- print 'In line %d: Error: %s' % (line_number, message)
-
- def _LineWarning(self, message, line_number):
- self.warning_count += 1
- print ('In line %d: Warning: Automatically fixing formatting: %s'
- % (line_number, message))
-
- def _CheckFormat(self, filename):
- if self.options.fix:
- fixed_lines = []
- with open(filename) as f:
- indent = 0
- line_number = 0
- for line in f:
- line_number += 1
- line = line.rstrip('\n')
- # Check for trailing whitespace.
- trailing_whitespace = self._TrailingWhitespace(line)
- if len(trailing_whitespace) > 0:
- if self.options.fix:
- line = line.rstrip()
- self._LineWarning('Trailing whitespace.', line_number)
- else:
- self._LineError('Trailing whitespace.', line_number)
- if self.options.fix:
- if len(line) == 0:
- fixed_lines += ['\n']
- continue
- else:
- if line == trailing_whitespace:
- # This also catches the case of an empty line.
- continue
- # Check for correct amount of leading whitespace.
- leading_whitespace = self._LeadingWhitespace(line)
- if leading_whitespace.count('\t') > 0:
- if self.options.fix:
- leading_whitespace = leading_whitespace.replace('\t', ' ')
- line = leading_whitespace + line.lstrip()
- self._LineWarning('Tab character found.', line_number)
- else:
- self._LineError('Tab character found.', line_number)
- if line[len(leading_whitespace)] in (']', '}'):
- indent -= 2
- if line[0] != '#': # Ignore 0-indented comments.
- if len(leading_whitespace) != indent:
- if self.options.fix:
- line = ' ' * indent + line.lstrip()
- self._LineWarning('Indentation should be ' + str(indent) +
- ' spaces.', line_number)
- else:
- self._LineError('Bad indentation. Should be ' + str(indent) +
- ' spaces.', line_number)
- if line[-1] in ('[', '{'):
- indent += 2
- if self.options.fix:
- fixed_lines.append(line + '\n')
-
- # If --fix is specified: backup the file (deleting any existing backup),
- # then write the fixed version with the old filename.
- if self.options.fix:
- if self.options.backup:
- backupfilename = filename + '.bak'
- if os.path.exists(backupfilename):
- os.remove(backupfilename)
- os.rename(filename, backupfilename)
- with open(filename, 'w') as f:
- f.writelines(fixed_lines)
-
- def Main(self, filename, options):
- try:
- with open(filename) as f:
- data = eval(f.read())
- except:
- import traceback
- traceback.print_exc(file=sys.stdout)
- self._Error('Invalid JSON syntax.')
- return
- if data == None:
- self._Error('Invalid JSON syntax.')
- return
- self.options = options
-
- # First part: check JSON structure.
-
- # Check (non-policy-specific) message definitions.
- messages = self._CheckContains(data, 'messages', dict,
- parent_element=None,
- container_name='The root element',
- offending=None)
- if messages is not None:
- for message in messages:
- self._CheckMessage(message, messages[message])
- if message.startswith('doc_feature_'):
- self.features.append(message[12:])
-
- # Check policy definitions.
- policy_definitions = self._CheckContains(data, 'policy_definitions', list,
- parent_element=None,
- container_name='The root element',
- offending=None)
- if policy_definitions is not None:
- policy_ids = set()
- for policy in policy_definitions:
- self._CheckPolicy(policy, False, policy_ids)
- self._CheckPolicyIDs(policy_ids)
-
- # Second part: check formatting.
- self._CheckFormat(filename)
-
- # Third part: summary and exit.
- print ('Finished checking %s. %d errors, %d warnings.' %
- (filename, self.error_count, self.warning_count))
- if self.options.stats:
- if self.num_groups > 0:
- print ('%d policies, %d of those in %d groups (containing on '
- 'average %.1f policies).' %
- (self.num_policies, self.num_policies_in_groups, self.num_groups,
- (1.0 * self.num_policies_in_groups / self.num_groups)))
- else:
- print self.num_policies, 'policies, 0 policy groups.'
- if self.error_count > 0:
- return 1
- return 0
-
- def Run(self, argv, filename=None):
- parser = optparse.OptionParser(
- usage='usage: %prog [options] filename',
- description='Syntax check a policy_templates.json file.')
- parser.add_option('--fix', action='store_true',
- help='Automatically fix formatting.')
- parser.add_option('--backup', action='store_true',
- help='Create backup of original file (before fixing).')
- parser.add_option('--stats', action='store_true',
- help='Generate statistics.')
- (options, args) = parser.parse_args(argv)
- if filename is None:
- if len(args) != 2:
- parser.print_help()
- sys.exit(1)
- filename = args[1]
- return self.Main(filename, options)
-
-
-if __name__ == '__main__':
- sys.exit(PolicyTemplateChecker().Run(sys.argv))