diff options
Diffstat (limited to 'examples/policy_checker')
-rw-r--r-- | examples/policy_checker/BUILD | 63 | ||||
-rw-r--r-- | examples/policy_checker/license_policy.bzl | 56 | ||||
-rw-r--r-- | examples/policy_checker/license_policy_check.bzl | 90 | ||||
-rw-r--r-- | examples/policy_checker/license_policy_provider.bzl | 24 |
4 files changed, 233 insertions, 0 deletions
diff --git a/examples/policy_checker/BUILD b/examples/policy_checker/BUILD new file mode 100644 index 0000000..49f77aa --- /dev/null +++ b/examples/policy_checker/BUILD @@ -0,0 +1,63 @@ +# Example of automated license policy definitions. + +load("@rules_license//examples/policy_checker:license_policy.bzl", "license_policy") +load("@rules_license//examples/policy_checker:license_policy_check.bzl", "license_policy_check") + +package(default_package_metadata = ["//:license", "//:package_info"]) + +# license_policy rules generally appear in a central location per workspace. That +# should be access controlled by the policy team. + +# A production service can use licenses with most conditions +license_policy( + name = "production_service", + conditions = [ + "notice", + "restricted_if_statically_linked", + ], +) + +# A mobile application usually can not allow end-user replacable libraries. +# So LGPL code (which is restricted_if_statically_linked) can not be used. +license_policy( + name = "mobile_application", + conditions = [ + "notice", + ], +) + +license_policy( + name = "special_allowlisted_app", + # There could be a allowlist of targets here. + conditions = [ + "notice", + "allowlist:acme_corp_paid", + ], +) + +# Now we might build checks of critical applications against policies +# +# Questions to consider? +# - Your organization migth want to fold these kinds of checks into +# wrapper macros around the rules which generate services and apps +# - You might want to distribute checks to rules alongside the products +# - Or, you might want to consolidate them in a single place where your +# compliance team owns them, as this example does + +license_policy_check( + name = "check_server", + policy = ":production_service", + target = "//examples/src:my_server", +) + + +# This is marked manual, so bazel test ... does not fail. Try it yourself with +# bazel build :check_violating_server +license_policy_check( + name = "check_violating_server", + policy = ":production_service", + tags = [ + "manual", + ], + target = "//examples/src:my_violating_server", +) diff --git a/examples/policy_checker/license_policy.bzl b/examples/policy_checker/license_policy.bzl new file mode 100644 index 0000000..51d6776 --- /dev/null +++ b/examples/policy_checker/license_policy.bzl @@ -0,0 +1,56 @@ +# Copyright 2020 Google LLC +# +# 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 +# +# https://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. + +"""license_policy rule. + +A license_policy names a set of conditions allowed in the union of all +license_kinds use by a target. The name of the rule is typically an +application type (e.g. production_server, mobile_application, ...) + +""" + +load( + "@rules_license//examples/policy_checker:license_policy_provider.bzl", + "LicensePolicyInfo" +) + +def _license_policy_impl(ctx): + provider = LicensePolicyInfo( + name = ctx.attr.name, + label = "@%s//%s:%s" % ( + ctx.label.workspace_name, + ctx.label.package, + ctx.label.name, + ), + conditions = ctx.attr.conditions, + ) + return [provider] + +_license_policy = rule( + implementation = _license_policy_impl, + attrs = { + "conditions": attr.string_list( + doc = "Conditions to be met when using software under this license." + + " Conditions are defined by the organization using this license.", + mandatory = True, + ), + }, +) + +def license_policy(name, conditions): + _license_policy( + name = name, + conditions = conditions, + applicable_licenses = [], + ) diff --git a/examples/policy_checker/license_policy_check.bzl b/examples/policy_checker/license_policy_check.bzl new file mode 100644 index 0000000..bb35eee --- /dev/null +++ b/examples/policy_checker/license_policy_check.bzl @@ -0,0 +1,90 @@ +# Copyright 2020 Google LLC +# +# 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 +# +# https://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. + +"""License compliance checking at analysis time.""" + +load( + "@rules_license//examples/policy_checker:license_policy_provider.bzl", + "LicensePolicyInfo", +) +load( + "@rules_license//rules:gather_licenses_info.bzl", + "gather_licenses_info", +) +load("@rules_license//rules:providers.bzl", "LicenseInfo") +load("@rules_license//rules/private:gathering_providers.bzl", "TransitiveLicensesInfo") + +# This is a crude example of the kind of thing which can be done. +def _license_policy_check_impl(ctx): + policy = ctx.attr.policy[LicensePolicyInfo] + allowed_conditions = policy.conditions + if TransitiveLicensesInfo in ctx.attr.target: + for license in ctx.attr.target[TransitiveLicensesInfo].licenses.to_list(): + for kind in license.license_kinds: + # print(kind.conditions) + for condition in kind.conditions: + if condition not in allowed_conditions: + fail("Condition %s violates policy %s" % ( + condition, + policy.label, + )) + + if LicenseInfo in ctx.attr.target: + for license in ctx.attr.target[LicenseInfo].licenses.to_list(): + for kind in license.license_kinds: + # print(kind.conditions) + for condition in kind.conditions: + if condition not in allowed_conditions: + fail("Condition %s violates policy %s" % ( + condition, + policy.label, + )) + return [DefaultInfo()] + +_license_policy_check = rule( + implementation = _license_policy_check_impl, + doc = """Internal implementation method for license_policy_check().""", + attrs = { + "policy": attr.label( + doc = """Policy definition.""", + mandatory = True, + providers = [LicensePolicyInfo], + ), + "target": attr.label( + doc = """Target to collect LicenseInfo for.""", + aspects = [gather_licenses_info], + mandatory = True, + allow_single_file = True, + ), + }, +) + +def license_policy_check(name, target, policy, **kwargs): + """Checks a target against a policy. + + Args: + name: The target. + target: A target to test for compliance with a policy + policy: A rule providing LicensePolicyInfo. + **kwargs: other args. + + Usage: + + license_policy_check( + name = "license_info", + target = ":my_app", + policy = "//my_org/compliance/policies:mobile_application", + ) + """ + _license_policy_check(name = name, target = target, policy = policy, **kwargs) diff --git a/examples/policy_checker/license_policy_provider.bzl b/examples/policy_checker/license_policy_provider.bzl new file mode 100644 index 0000000..caecce8 --- /dev/null +++ b/examples/policy_checker/license_policy_provider.bzl @@ -0,0 +1,24 @@ +# Copyright 2020 Google LLC +# +# 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 +# +# https://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. + +"""LicensePolicyProvider.""" + +LicensePolicyInfo = provider( + doc = """Declares a policy name and the license conditions allowable under it.""", + fields = { + "conditions": "List of conditions to be met when using this software.", + "label": "The full path to the license policy definition.", + "name": "License policy name", + }, +) |