blob: 60bdf48e227c91100d460da0e78a01ed44cc30bb [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"
19 "strings"
20)
21
22var (
Bob Badour67d8ae32022-01-10 18:32:54 -080023 // RecognizedAnnotations identifies the set of annotations that have
24 // meaning for compliance policy.
25 RecognizedAnnotations = map[string]string{
26 // used in readgraph.go to avoid creating 1000's of copies of the below 3 strings.
27 "static": "static",
28 "dynamic": "dynamic",
29 "toolchain": "toolchain",
30 }
31
Bob Badoure6fdd142021-12-09 22:10:43 -080032 // SafePathPrefixes maps the path prefixes presumed not to contain any
33 // proprietary or confidential pathnames to whether to strip the prefix
34 // from the path when used as the library name for notices.
35 SafePathPrefixes = map[string]bool{
Colin Cross35f79c32022-01-27 15:18:52 -080036 "external/": true,
37 "art/": false,
38 "build/": false,
39 "cts/": false,
40 "dalvik/": false,
41 "developers/": false,
Bob Badoure6fdd142021-12-09 22:10:43 -080042 "development/": false,
Colin Cross35f79c32022-01-27 15:18:52 -080043 "frameworks/": false,
44 "packages/": true,
45 "prebuilts/": false,
46 "sdk/": false,
47 "system/": false,
48 "test/": false,
49 "toolchain/": false,
50 "tools/": false,
Bob Badoure6fdd142021-12-09 22:10:43 -080051 }
52
53 // SafePrebuiltPrefixes maps the regular expression to match a prebuilt
54 // containing the path of a safe prefix to the safe prefix.
55 SafePrebuiltPrefixes = make(map[*regexp.Regexp]string)
56
Bob Badour9ee7d032021-10-25 16:51:48 -070057 // ImpliesUnencumbered lists the condition names representing an author attempt to disclaim copyright.
Bob Badour103eb0f2022-01-10 13:50:57 -080058 ImpliesUnencumbered = LicenseConditionSet(UnencumberedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070059
60 // ImpliesPermissive lists the condition names representing copyrighted but "licensed without policy requirements".
Bob Badour103eb0f2022-01-10 13:50:57 -080061 ImpliesPermissive = LicenseConditionSet(PermissiveCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070062
63 // ImpliesNotice lists the condition names implying a notice or attribution policy.
Bob Badour103eb0f2022-01-10 13:50:57 -080064 ImpliesNotice = LicenseConditionSet(UnencumberedCondition | PermissiveCondition | NoticeCondition | ReciprocalCondition |
Colin Cross35f79c32022-01-27 15:18:52 -080065 RestrictedCondition | RestrictedClasspathExceptionCondition | WeaklyRestrictedCondition |
66 ProprietaryCondition | ByExceptionOnlyCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070067
68 // ImpliesReciprocal lists the condition names implying a local source-sharing policy.
Bob Badour103eb0f2022-01-10 13:50:57 -080069 ImpliesReciprocal = LicenseConditionSet(ReciprocalCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070070
71 // Restricted lists the condition names implying an infectious source-sharing policy.
Bob Badour103eb0f2022-01-10 13:50:57 -080072 ImpliesRestricted = LicenseConditionSet(RestrictedCondition | RestrictedClasspathExceptionCondition | WeaklyRestrictedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070073
74 // ImpliesProprietary lists the condition names implying a confidentiality policy.
Bob Badour103eb0f2022-01-10 13:50:57 -080075 ImpliesProprietary = LicenseConditionSet(ProprietaryCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070076
77 // ImpliesByExceptionOnly lists the condition names implying a policy for "license review and approval before use".
Bob Badour103eb0f2022-01-10 13:50:57 -080078 ImpliesByExceptionOnly = LicenseConditionSet(ProprietaryCondition | ByExceptionOnlyCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070079
80 // ImpliesPrivate lists the condition names implying a source-code privacy policy.
Bob Badour103eb0f2022-01-10 13:50:57 -080081 ImpliesPrivate = LicenseConditionSet(ProprietaryCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070082
83 // ImpliesShared lists the condition names implying a source-code sharing policy.
Bob Badour103eb0f2022-01-10 13:50:57 -080084 ImpliesShared = LicenseConditionSet(ReciprocalCondition | RestrictedCondition | RestrictedClasspathExceptionCondition | WeaklyRestrictedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070085)
86
87var (
88 anyLgpl = regexp.MustCompile(`^SPDX-license-identifier-LGPL.*`)
89 versionedGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL-\p{N}.*`)
90 genericGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL$`)
91 ccBySa = regexp.MustCompile(`^SPDX-license-identifier-CC-BY.*-SA.*`)
92)
93
Bob Badoure6fdd142021-12-09 22:10:43 -080094func init() {
95 for prefix := range SafePathPrefixes {
96 if prefix == "prebuilts/" {
97 continue
98 }
99 r := regexp.MustCompile("^prebuilts/[^ ]*/" + prefix)
100 SafePrebuiltPrefixes[r] = prefix
101 }
102}
Bob Badour103eb0f2022-01-10 13:50:57 -0800103
104// LicenseConditionSetFromNames returns a set containing the recognized `names` and
105// silently ignoring or discarding the unrecognized `names`.
106func LicenseConditionSetFromNames(tn *TargetNode, names ...string) LicenseConditionSet {
107 cs := NewLicenseConditionSet()
108 for _, name := range names {
109 if name == "restricted" {
110 if 0 == len(tn.LicenseKinds()) {
111 cs = cs.Plus(RestrictedCondition)
112 continue
113 }
114 hasLgpl := false
115 hasClasspath := false
116 hasGeneric := false
117 for _, kind := range tn.LicenseKinds() {
118 if strings.HasSuffix(kind, "-with-classpath-exception") {
119 cs = cs.Plus(RestrictedClasspathExceptionCondition)
120 hasClasspath = true
121 } else if anyLgpl.MatchString(kind) {
122 cs = cs.Plus(WeaklyRestrictedCondition)
123 hasLgpl = true
124 } else if versionedGpl.MatchString(kind) {
125 cs = cs.Plus(RestrictedCondition)
126 } else if genericGpl.MatchString(kind) {
127 hasGeneric = true
128 } else if kind == "legacy_restricted" || ccBySa.MatchString(kind) {
129 cs = cs.Plus(RestrictedCondition)
130 } else {
131 cs = cs.Plus(RestrictedCondition)
132 }
133 }
134 if hasGeneric && !hasLgpl && !hasClasspath {
135 cs = cs.Plus(RestrictedCondition)
136 }
137 continue
138 }
139 if lc, ok := RecognizedConditionNames[name]; ok {
140 cs |= LicenseConditionSet(lc)
141 }
142 }
143 return cs
144}
145
Bob Badour103eb0f2022-01-10 13:50:57 -0800146// Resolution happens in three phases:
Bob Badour9ee7d032021-10-25 16:51:48 -0700147//
Bob Badour103eb0f2022-01-10 13:50:57 -0800148// 1. A bottom-up traversal propagates (restricted) license conditions up to
149// targets from dendencies as needed.
Bob Badour9ee7d032021-10-25 16:51:48 -0700150//
Bob Badour103eb0f2022-01-10 13:50:57 -0800151// 2. For each condition of interest, a top-down traversal propagates
152// (restricted) conditions down from targets into linked dependencies.
Bob Badour9ee7d032021-10-25 16:51:48 -0700153//
Bob Badour103eb0f2022-01-10 13:50:57 -0800154// 3. Finally, a walk of the shipped target nodes attaches resolutions to the
155// ancestor nodes from the root down to and including the first non-container.
Bob Badour9ee7d032021-10-25 16:51:48 -0700156//
Bob Badour103eb0f2022-01-10 13:50:57 -0800157// e.g. If a disk image contains a binary bin1 that links a library liba, the
158// notice requirement for liba gets attached to the disk image and to bin1.
159// Because liba doesn't actually get shipped as a separate artifact, but only
160// as bits in bin1, it has no actions 'attached' to it. The actions attached
161// to the image and to bin1 'act on' liba by providing notice.
Bob Badour9ee7d032021-10-25 16:51:48 -0700162//
Bob Badour103eb0f2022-01-10 13:50:57 -0800163// The behavior of the 3 phases gets controlled by the 3 functions below.
Bob Badour9ee7d032021-10-25 16:51:48 -0700164//
Bob Badour103eb0f2022-01-10 13:50:57 -0800165// The first function controls what happens during the bottom-up propagation.
166// Restricted conditions propagate up all non-toolchain dependencies; except,
167// some do not propagate up dynamic links, which may depend on whether the
168// modules are independent.
169//
170// The second function controls what happens during the top-down propagation.
171// Restricted conditions propagate down as above with the added caveat that
172// inherited restricted conditions do not propagate from pure aggregates to
173// their dependencies.
174//
175// The final function controls which conditions apply/get attached to ancestors
176// depending on the types of dependencies involved. All conditions apply across
177// normal derivation dependencies. No conditions apply across toolchain
178// dependencies. Some restricted conditions apply across dynamic link
179// dependencies.
Bob Badour9ee7d032021-10-25 16:51:48 -0700180//
181// Not all restricted licenses are create equal. Some have special rules or
182// exceptions. e.g. LGPL or "with classpath excption".
183
Bob Badour103eb0f2022-01-10 13:50:57 -0800184// depConditionsPropagatingToTarget returns the conditions which propagate up an
Bob Badour9ee7d032021-10-25 16:51:48 -0700185// edge from dependency to target.
186//
Bob Badour103eb0f2022-01-10 13:50:57 -0800187// This function sets the policy for the bottom-up propagation and how conditions
Bob Badour9ee7d032021-10-25 16:51:48 -0700188// flow up the graph from dependencies to targets.
189//
190// If a pure aggregation is built into a derivative work that is not a pure
191// aggregation, per policy it ceases to be a pure aggregation in the context of
192// that derivative work. The `treatAsAggregate` parameter will be false for
193// non-aggregates and for aggregates in non-aggregate contexts.
Bob Badour103eb0f2022-01-10 13:50:57 -0800194func depConditionsPropagatingToTarget(lg *LicenseGraph, e *TargetEdge, depConditions LicenseConditionSet, treatAsAggregate bool) LicenseConditionSet {
195 result := LicenseConditionSet(0x0000)
Bob Badour9ee7d032021-10-25 16:51:48 -0700196 if edgeIsDerivation(e) {
Bob Badour103eb0f2022-01-10 13:50:57 -0800197 result |= depConditions & ImpliesRestricted
Bob Badour9ee7d032021-10-25 16:51:48 -0700198 return result
199 }
200 if !edgeIsDynamicLink(e) {
201 return result
202 }
203
Bob Badour103eb0f2022-01-10 13:50:57 -0800204 result |= depConditions & LicenseConditionSet(RestrictedCondition)
Colin Cross35f79c32022-01-27 15:18:52 -0800205 if 0 != (depConditions&LicenseConditionSet(RestrictedClasspathExceptionCondition)) && !edgeNodesAreIndependentModules(e) {
Bob Badour103eb0f2022-01-10 13:50:57 -0800206 result |= LicenseConditionSet(RestrictedClasspathExceptionCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -0700207 }
208 return result
209}
210
Bob Badour103eb0f2022-01-10 13:50:57 -0800211// targetConditionsPropagatingToDep returns the conditions which propagate down
Bob Badour9ee7d032021-10-25 16:51:48 -0700212// an edge from target to dependency.
213//
214// This function sets the policy for the top-down traversal and how conditions
215// flow down the graph from targets to dependencies.
216//
217// If a pure aggregation is built into a derivative work that is not a pure
218// aggregation, per policy it ceases to be a pure aggregation in the context of
219// that derivative work. The `treatAsAggregate` parameter will be false for
220// non-aggregates and for aggregates in non-aggregate contexts.
Bob Badourc8178452022-01-31 13:05:53 -0800221func targetConditionsPropagatingToDep(lg *LicenseGraph, e *TargetEdge, targetConditions LicenseConditionSet, treatAsAggregate bool, conditionsFn TraceConditions) LicenseConditionSet {
Bob Badour103eb0f2022-01-10 13:50:57 -0800222 result := targetConditions
Bob Badour9ee7d032021-10-25 16:51:48 -0700223
224 // reverse direction -- none of these apply to things depended-on, only to targets depending-on.
Bob Badour103eb0f2022-01-10 13:50:57 -0800225 result = result.Minus(UnencumberedCondition, PermissiveCondition, NoticeCondition, ReciprocalCondition, ProprietaryCondition, ByExceptionOnlyCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -0700226
227 if !edgeIsDerivation(e) && !edgeIsDynamicLink(e) {
228 // target is not a derivative work of dependency and is not linked to dependency
Bob Badour103eb0f2022-01-10 13:50:57 -0800229 result = result.Difference(ImpliesRestricted)
Bob Badour9ee7d032021-10-25 16:51:48 -0700230 return result
231 }
232 if treatAsAggregate {
233 // If the author of a pure aggregate licenses it restricted, apply restricted to immediate dependencies.
234 // Otherwise, restricted does not propagate back down to dependencies.
Bob Badourc8178452022-01-31 13:05:53 -0800235 if !conditionsFn(e.target).MatchesAnySet(ImpliesRestricted) {
Bob Badour103eb0f2022-01-10 13:50:57 -0800236 result = result.Difference(ImpliesRestricted)
Bob Badour9ee7d032021-10-25 16:51:48 -0700237 }
238 return result
239 }
240 if edgeIsDerivation(e) {
241 return result
242 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800243 result = result.Minus(WeaklyRestrictedCondition)
244 if edgeNodesAreIndependentModules(e) {
245 result = result.Minus(RestrictedClasspathExceptionCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -0700246 }
247 return result
248}
249
Bob Badour103eb0f2022-01-10 13:50:57 -0800250// conditionsAttachingAcrossEdge returns the subset of conditions in `universe`
251// that apply across edge `e`.
252//
253// This function sets the policy for attaching actions to ancestor nodes in the
254// final resolution walk.
255func conditionsAttachingAcrossEdge(lg *LicenseGraph, e *TargetEdge, universe LicenseConditionSet) LicenseConditionSet {
256 result := universe
257 if edgeIsDerivation(e) {
258 return result
259 }
260 if !edgeIsDynamicLink(e) {
261 return NewLicenseConditionSet()
262 }
263
264 result &= LicenseConditionSet(RestrictedCondition | RestrictedClasspathExceptionCondition)
Colin Cross35f79c32022-01-27 15:18:52 -0800265 if 0 != (result&LicenseConditionSet(RestrictedClasspathExceptionCondition)) && edgeNodesAreIndependentModules(e) {
Bob Badour103eb0f2022-01-10 13:50:57 -0800266 result &= LicenseConditionSet(RestrictedCondition)
267 }
268 return result
269}
270
Bob Badour9ee7d032021-10-25 16:51:48 -0700271// edgeIsDynamicLink returns true for edges representing shared libraries
272// linked dynamically at runtime.
Bob Badour103eb0f2022-01-10 13:50:57 -0800273func edgeIsDynamicLink(e *TargetEdge) bool {
274 return e.annotations.HasAnnotation("dynamic")
Bob Badour9ee7d032021-10-25 16:51:48 -0700275}
276
277// edgeIsDerivation returns true for edges where the target is a derivative
278// work of dependency.
Bob Badour103eb0f2022-01-10 13:50:57 -0800279func edgeIsDerivation(e *TargetEdge) bool {
280 isDynamic := e.annotations.HasAnnotation("dynamic")
281 isToolchain := e.annotations.HasAnnotation("toolchain")
Bob Badour9ee7d032021-10-25 16:51:48 -0700282 return !isDynamic && !isToolchain
283}
284
285// edgeNodesAreIndependentModules returns true for edges where the target and
286// dependency are independent modules.
Bob Badour103eb0f2022-01-10 13:50:57 -0800287func edgeNodesAreIndependentModules(e *TargetEdge) bool {
288 return e.target.PackageName() != e.dependency.PackageName()
Bob Badour9ee7d032021-10-25 16:51:48 -0700289}