blob: 63da73114dbf8bc2275ad602db101b4689f0b48e [file] [log] [blame]
Bob Badour9ee7d032021-10-25 16:51:48 -07001// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package compliance
16
17import (
18 "regexp"
Bob Badour9ee7d032021-10-25 16:51:48 -070019)
20
21var (
Bob Badour67d8ae32022-01-10 18:32:54 -080022 // RecognizedAnnotations identifies the set of annotations that have
23 // meaning for compliance policy.
24 RecognizedAnnotations = map[string]string{
25 // used in readgraph.go to avoid creating 1000's of copies of the below 3 strings.
26 "static": "static",
27 "dynamic": "dynamic",
28 "toolchain": "toolchain",
29 }
30
Bob Badoure6fdd142021-12-09 22:10:43 -080031 // SafePathPrefixes maps the path prefixes presumed not to contain any
32 // proprietary or confidential pathnames to whether to strip the prefix
33 // from the path when used as the library name for notices.
34 SafePathPrefixes = map[string]bool{
Colin Cross35f79c32022-01-27 15:18:52 -080035 "external/": true,
36 "art/": false,
37 "build/": false,
38 "cts/": false,
39 "dalvik/": false,
40 "developers/": false,
Bob Badoure6fdd142021-12-09 22:10:43 -080041 "development/": false,
Colin Cross35f79c32022-01-27 15:18:52 -080042 "frameworks/": false,
43 "packages/": true,
44 "prebuilts/": false,
45 "sdk/": false,
46 "system/": false,
47 "test/": false,
48 "toolchain/": false,
49 "tools/": false,
Bob Badoure6fdd142021-12-09 22:10:43 -080050 }
51
52 // SafePrebuiltPrefixes maps the regular expression to match a prebuilt
53 // containing the path of a safe prefix to the safe prefix.
54 SafePrebuiltPrefixes = make(map[*regexp.Regexp]string)
55
Bob Badour9ee7d032021-10-25 16:51:48 -070056 // ImpliesUnencumbered lists the condition names representing an author attempt to disclaim copyright.
Bob Badour103eb0f2022-01-10 13:50:57 -080057 ImpliesUnencumbered = LicenseConditionSet(UnencumberedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070058
59 // ImpliesPermissive lists the condition names representing copyrighted but "licensed without policy requirements".
Bob Badour103eb0f2022-01-10 13:50:57 -080060 ImpliesPermissive = LicenseConditionSet(PermissiveCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070061
62 // ImpliesNotice lists the condition names implying a notice or attribution policy.
Bob Badour103eb0f2022-01-10 13:50:57 -080063 ImpliesNotice = LicenseConditionSet(UnencumberedCondition | PermissiveCondition | NoticeCondition | ReciprocalCondition |
Bob Badour10f5c482022-09-20 21:44:17 -070064 RestrictedCondition | WeaklyRestrictedCondition | ProprietaryCondition | ByExceptionOnlyCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070065
66 // ImpliesReciprocal lists the condition names implying a local source-sharing policy.
Bob Badour103eb0f2022-01-10 13:50:57 -080067 ImpliesReciprocal = LicenseConditionSet(ReciprocalCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070068
69 // Restricted lists the condition names implying an infectious source-sharing policy.
Bob Badour10f5c482022-09-20 21:44:17 -070070 ImpliesRestricted = LicenseConditionSet(RestrictedCondition | WeaklyRestrictedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070071
72 // ImpliesProprietary lists the condition names implying a confidentiality policy.
Bob Badour103eb0f2022-01-10 13:50:57 -080073 ImpliesProprietary = LicenseConditionSet(ProprietaryCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070074
75 // ImpliesByExceptionOnly lists the condition names implying a policy for "license review and approval before use".
Bob Badour103eb0f2022-01-10 13:50:57 -080076 ImpliesByExceptionOnly = LicenseConditionSet(ProprietaryCondition | ByExceptionOnlyCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070077
78 // ImpliesPrivate lists the condition names implying a source-code privacy policy.
Bob Badour103eb0f2022-01-10 13:50:57 -080079 ImpliesPrivate = LicenseConditionSet(ProprietaryCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070080
81 // ImpliesShared lists the condition names implying a source-code sharing policy.
Bob Badour10f5c482022-09-20 21:44:17 -070082 ImpliesShared = LicenseConditionSet(ReciprocalCondition | RestrictedCondition | WeaklyRestrictedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070083)
84
85var (
86 anyLgpl = regexp.MustCompile(`^SPDX-license-identifier-LGPL.*`)
87 versionedGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL-\p{N}.*`)
88 genericGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL$`)
89 ccBySa = regexp.MustCompile(`^SPDX-license-identifier-CC-BY.*-SA.*`)
90)
91
Bob Badoure6fdd142021-12-09 22:10:43 -080092func init() {
93 for prefix := range SafePathPrefixes {
94 if prefix == "prebuilts/" {
95 continue
96 }
97 r := regexp.MustCompile("^prebuilts/[^ ]*/" + prefix)
98 SafePrebuiltPrefixes[r] = prefix
99 }
100}
Bob Badour103eb0f2022-01-10 13:50:57 -0800101
102// LicenseConditionSetFromNames returns a set containing the recognized `names` and
103// silently ignoring or discarding the unrecognized `names`.
104func LicenseConditionSetFromNames(tn *TargetNode, names ...string) LicenseConditionSet {
105 cs := NewLicenseConditionSet()
106 for _, name := range names {
107 if name == "restricted" {
108 if 0 == len(tn.LicenseKinds()) {
109 cs = cs.Plus(RestrictedCondition)
110 continue
111 }
112 hasLgpl := false
Bob Badour103eb0f2022-01-10 13:50:57 -0800113 hasGeneric := false
114 for _, kind := range tn.LicenseKinds() {
Bob Badour10f5c482022-09-20 21:44:17 -0700115 if anyLgpl.MatchString(kind) {
Bob Badour103eb0f2022-01-10 13:50:57 -0800116 cs = cs.Plus(WeaklyRestrictedCondition)
117 hasLgpl = true
118 } else if versionedGpl.MatchString(kind) {
119 cs = cs.Plus(RestrictedCondition)
120 } else if genericGpl.MatchString(kind) {
121 hasGeneric = true
122 } else if kind == "legacy_restricted" || ccBySa.MatchString(kind) {
123 cs = cs.Plus(RestrictedCondition)
124 } else {
125 cs = cs.Plus(RestrictedCondition)
126 }
127 }
Bob Badour10f5c482022-09-20 21:44:17 -0700128 if hasGeneric && !hasLgpl {
Bob Badour103eb0f2022-01-10 13:50:57 -0800129 cs = cs.Plus(RestrictedCondition)
130 }
131 continue
132 }
133 if lc, ok := RecognizedConditionNames[name]; ok {
134 cs |= LicenseConditionSet(lc)
135 }
136 }
137 return cs
138}
139
Bob Badour103eb0f2022-01-10 13:50:57 -0800140// Resolution happens in three phases:
Bob Badour9ee7d032021-10-25 16:51:48 -0700141//
Bob Badour103eb0f2022-01-10 13:50:57 -0800142// 1. A bottom-up traversal propagates (restricted) license conditions up to
143// targets from dendencies as needed.
Bob Badour9ee7d032021-10-25 16:51:48 -0700144//
Bob Badour103eb0f2022-01-10 13:50:57 -0800145// 2. For each condition of interest, a top-down traversal propagates
146// (restricted) conditions down from targets into linked dependencies.
Bob Badour9ee7d032021-10-25 16:51:48 -0700147//
Bob Badour103eb0f2022-01-10 13:50:57 -0800148// 3. Finally, a walk of the shipped target nodes attaches resolutions to the
149// ancestor nodes from the root down to and including the first non-container.
Bob Badour9ee7d032021-10-25 16:51:48 -0700150//
Bob Badour103eb0f2022-01-10 13:50:57 -0800151// e.g. If a disk image contains a binary bin1 that links a library liba, the
152// notice requirement for liba gets attached to the disk image and to bin1.
153// Because liba doesn't actually get shipped as a separate artifact, but only
154// as bits in bin1, it has no actions 'attached' to it. The actions attached
155// to the image and to bin1 'act on' liba by providing notice.
Bob Badour9ee7d032021-10-25 16:51:48 -0700156//
Bob Badour103eb0f2022-01-10 13:50:57 -0800157// The behavior of the 3 phases gets controlled by the 3 functions below.
Bob Badour9ee7d032021-10-25 16:51:48 -0700158//
Bob Badour103eb0f2022-01-10 13:50:57 -0800159// The first function controls what happens during the bottom-up propagation.
160// Restricted conditions propagate up all non-toolchain dependencies; except,
161// some do not propagate up dynamic links, which may depend on whether the
162// modules are independent.
163//
164// The second function controls what happens during the top-down propagation.
165// Restricted conditions propagate down as above with the added caveat that
166// inherited restricted conditions do not propagate from pure aggregates to
167// their dependencies.
168//
169// The final function controls which conditions apply/get attached to ancestors
170// depending on the types of dependencies involved. All conditions apply across
171// normal derivation dependencies. No conditions apply across toolchain
172// dependencies. Some restricted conditions apply across dynamic link
173// dependencies.
Bob Badour9ee7d032021-10-25 16:51:48 -0700174//
175// Not all restricted licenses are create equal. Some have special rules or
176// exceptions. e.g. LGPL or "with classpath excption".
177
Bob Badour103eb0f2022-01-10 13:50:57 -0800178// depConditionsPropagatingToTarget returns the conditions which propagate up an
Bob Badour9ee7d032021-10-25 16:51:48 -0700179// edge from dependency to target.
180//
Bob Badour103eb0f2022-01-10 13:50:57 -0800181// This function sets the policy for the bottom-up propagation and how conditions
Bob Badour9ee7d032021-10-25 16:51:48 -0700182// flow up the graph from dependencies to targets.
183//
184// If a pure aggregation is built into a derivative work that is not a pure
185// aggregation, per policy it ceases to be a pure aggregation in the context of
186// that derivative work. The `treatAsAggregate` parameter will be false for
187// non-aggregates and for aggregates in non-aggregate contexts.
Bob Badour103eb0f2022-01-10 13:50:57 -0800188func depConditionsPropagatingToTarget(lg *LicenseGraph, e *TargetEdge, depConditions LicenseConditionSet, treatAsAggregate bool) LicenseConditionSet {
189 result := LicenseConditionSet(0x0000)
Bob Badour9ee7d032021-10-25 16:51:48 -0700190 if edgeIsDerivation(e) {
Bob Badour103eb0f2022-01-10 13:50:57 -0800191 result |= depConditions & ImpliesRestricted
Bob Badour9ee7d032021-10-25 16:51:48 -0700192 return result
193 }
194 if !edgeIsDynamicLink(e) {
195 return result
196 }
197
Bob Badour103eb0f2022-01-10 13:50:57 -0800198 result |= depConditions & LicenseConditionSet(RestrictedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -0700199 return result
200}
201
Bob Badour103eb0f2022-01-10 13:50:57 -0800202// targetConditionsPropagatingToDep returns the conditions which propagate down
Bob Badour9ee7d032021-10-25 16:51:48 -0700203// an edge from target to dependency.
204//
205// This function sets the policy for the top-down traversal and how conditions
206// flow down the graph from targets to dependencies.
207//
208// If a pure aggregation is built into a derivative work that is not a pure
209// aggregation, per policy it ceases to be a pure aggregation in the context of
210// that derivative work. The `treatAsAggregate` parameter will be false for
211// non-aggregates and for aggregates in non-aggregate contexts.
Bob Badourc8178452022-01-31 13:05:53 -0800212func targetConditionsPropagatingToDep(lg *LicenseGraph, e *TargetEdge, targetConditions LicenseConditionSet, treatAsAggregate bool, conditionsFn TraceConditions) LicenseConditionSet {
Bob Badour103eb0f2022-01-10 13:50:57 -0800213 result := targetConditions
Bob Badour9ee7d032021-10-25 16:51:48 -0700214
215 // reverse direction -- none of these apply to things depended-on, only to targets depending-on.
Bob Badour103eb0f2022-01-10 13:50:57 -0800216 result = result.Minus(UnencumberedCondition, PermissiveCondition, NoticeCondition, ReciprocalCondition, ProprietaryCondition, ByExceptionOnlyCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -0700217
218 if !edgeIsDerivation(e) && !edgeIsDynamicLink(e) {
219 // target is not a derivative work of dependency and is not linked to dependency
Bob Badour103eb0f2022-01-10 13:50:57 -0800220 result = result.Difference(ImpliesRestricted)
Bob Badour9ee7d032021-10-25 16:51:48 -0700221 return result
222 }
223 if treatAsAggregate {
224 // If the author of a pure aggregate licenses it restricted, apply restricted to immediate dependencies.
225 // Otherwise, restricted does not propagate back down to dependencies.
Bob Badourc8178452022-01-31 13:05:53 -0800226 if !conditionsFn(e.target).MatchesAnySet(ImpliesRestricted) {
Bob Badour103eb0f2022-01-10 13:50:57 -0800227 result = result.Difference(ImpliesRestricted)
Bob Badour9ee7d032021-10-25 16:51:48 -0700228 }
229 return result
230 }
231 if edgeIsDerivation(e) {
232 return result
233 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800234 result = result.Minus(WeaklyRestrictedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -0700235 return result
236}
237
Bob Badour103eb0f2022-01-10 13:50:57 -0800238// conditionsAttachingAcrossEdge returns the subset of conditions in `universe`
239// that apply across edge `e`.
240//
241// This function sets the policy for attaching actions to ancestor nodes in the
242// final resolution walk.
243func conditionsAttachingAcrossEdge(lg *LicenseGraph, e *TargetEdge, universe LicenseConditionSet) LicenseConditionSet {
244 result := universe
245 if edgeIsDerivation(e) {
246 return result
247 }
248 if !edgeIsDynamicLink(e) {
249 return NewLicenseConditionSet()
250 }
251
Bob Badour10f5c482022-09-20 21:44:17 -0700252 result &= LicenseConditionSet(RestrictedCondition)
Bob Badour103eb0f2022-01-10 13:50:57 -0800253 return result
254}
255
Bob Badour9ee7d032021-10-25 16:51:48 -0700256// edgeIsDynamicLink returns true for edges representing shared libraries
257// linked dynamically at runtime.
Bob Badour103eb0f2022-01-10 13:50:57 -0800258func edgeIsDynamicLink(e *TargetEdge) bool {
259 return e.annotations.HasAnnotation("dynamic")
Bob Badour9ee7d032021-10-25 16:51:48 -0700260}
261
262// edgeIsDerivation returns true for edges where the target is a derivative
263// work of dependency.
Bob Badour103eb0f2022-01-10 13:50:57 -0800264func edgeIsDerivation(e *TargetEdge) bool {
265 isDynamic := e.annotations.HasAnnotation("dynamic")
266 isToolchain := e.annotations.HasAnnotation("toolchain")
Bob Badour9ee7d032021-10-25 16:51:48 -0700267 return !isDynamic && !isToolchain
268}