blob: 2787698774f07c9e2021cf419fe92198d05f78f4 [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 Badour113c92b2022-09-23 11:27:24 -070019 "sort"
20 "strings"
Bob Badour9ee7d032021-10-25 16:51:48 -070021)
22
23var (
Bob Badour67d8ae32022-01-10 18:32:54 -080024 // RecognizedAnnotations identifies the set of annotations that have
25 // meaning for compliance policy.
26 RecognizedAnnotations = map[string]string{
27 // used in readgraph.go to avoid creating 1000's of copies of the below 3 strings.
28 "static": "static",
29 "dynamic": "dynamic",
30 "toolchain": "toolchain",
31 }
32
Bob Badoure6fdd142021-12-09 22:10:43 -080033 // SafePathPrefixes maps the path prefixes presumed not to contain any
34 // proprietary or confidential pathnames to whether to strip the prefix
35 // from the path when used as the library name for notices.
36 SafePathPrefixes = map[string]bool{
Bob Badour113c92b2022-09-23 11:27:24 -070037 "external/": true,
38 "art/": false,
39 "build/": false,
40 "cts/": false,
41 "dalvik/": false,
42 "developers/": false,
43 "development/": false,
44 "frameworks/": false,
45 "packages/": true,
46 "prebuilts/module_sdk/": true,
47 "prebuilts/": false,
48 "sdk/": false,
49 "system/": false,
50 "test/": false,
51 "toolchain/": false,
52 "tools/": false,
Bob Badoure6fdd142021-12-09 22:10:43 -080053 }
54
55 // SafePrebuiltPrefixes maps the regular expression to match a prebuilt
56 // containing the path of a safe prefix to the safe prefix.
57 SafePrebuiltPrefixes = make(map[*regexp.Regexp]string)
58
Bob Badour113c92b2022-09-23 11:27:24 -070059 // OrderedSafePrebuiltPrefixes lists the SafePrebuiltPrefixes ordered by
60 // increasing length.
61 OrderedSafePrebuiltPrefixes = make([]*regexp.Regexp, 0, 0)
62
Bob Badour9ee7d032021-10-25 16:51:48 -070063 // ImpliesUnencumbered lists the condition names representing an author attempt to disclaim copyright.
Bob Badour103eb0f2022-01-10 13:50:57 -080064 ImpliesUnencumbered = LicenseConditionSet(UnencumberedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070065
66 // ImpliesPermissive lists the condition names representing copyrighted but "licensed without policy requirements".
Bob Badour103eb0f2022-01-10 13:50:57 -080067 ImpliesPermissive = LicenseConditionSet(PermissiveCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070068
69 // ImpliesNotice lists the condition names implying a notice or attribution policy.
Bob Badour103eb0f2022-01-10 13:50:57 -080070 ImpliesNotice = LicenseConditionSet(UnencumberedCondition | PermissiveCondition | NoticeCondition | ReciprocalCondition |
Bob Badour10f5c482022-09-20 21:44:17 -070071 RestrictedCondition | WeaklyRestrictedCondition | ProprietaryCondition | ByExceptionOnlyCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070072
73 // ImpliesReciprocal lists the condition names implying a local source-sharing policy.
Bob Badour103eb0f2022-01-10 13:50:57 -080074 ImpliesReciprocal = LicenseConditionSet(ReciprocalCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070075
76 // Restricted lists the condition names implying an infectious source-sharing policy.
Bob Badour10f5c482022-09-20 21:44:17 -070077 ImpliesRestricted = LicenseConditionSet(RestrictedCondition | WeaklyRestrictedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070078
79 // ImpliesProprietary lists the condition names implying a confidentiality policy.
Bob Badour103eb0f2022-01-10 13:50:57 -080080 ImpliesProprietary = LicenseConditionSet(ProprietaryCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070081
82 // ImpliesByExceptionOnly lists the condition names implying a policy for "license review and approval before use".
Bob Badour103eb0f2022-01-10 13:50:57 -080083 ImpliesByExceptionOnly = LicenseConditionSet(ProprietaryCondition | ByExceptionOnlyCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070084
85 // ImpliesPrivate lists the condition names implying a source-code privacy policy.
Bob Badour103eb0f2022-01-10 13:50:57 -080086 ImpliesPrivate = LicenseConditionSet(ProprietaryCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070087
88 // ImpliesShared lists the condition names implying a source-code sharing policy.
Bob Badour10f5c482022-09-20 21:44:17 -070089 ImpliesShared = LicenseConditionSet(ReciprocalCondition | RestrictedCondition | WeaklyRestrictedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070090)
91
92var (
93 anyLgpl = regexp.MustCompile(`^SPDX-license-identifier-LGPL.*`)
94 versionedGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL-\p{N}.*`)
95 genericGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL$`)
96 ccBySa = regexp.MustCompile(`^SPDX-license-identifier-CC-BY.*-SA.*`)
97)
98
Bob Badour113c92b2022-09-23 11:27:24 -070099// byIncreasingLength implements `sort.Interface` to order regular expressions by increasing length.
100type byIncreasingLength []*regexp.Regexp
101
102func (l byIncreasingLength) Len() int { return len(l) }
103func (l byIncreasingLength) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
104func (l byIncreasingLength) Less(i, j int) bool {
105 ri := l[i].String()
106 rj := l[j].String()
107 if len(ri) == len(rj) {
108 return ri < rj
109 }
110 return len(ri) < len(rj)
111}
112
Bob Badoure6fdd142021-12-09 22:10:43 -0800113func init() {
114 for prefix := range SafePathPrefixes {
Bob Badour113c92b2022-09-23 11:27:24 -0700115 if strings.HasPrefix(prefix, "prebuilts/") {
Bob Badoure6fdd142021-12-09 22:10:43 -0800116 continue
117 }
Bob Badour113c92b2022-09-23 11:27:24 -0700118 r := regexp.MustCompile("^prebuilts/(?:runtime/mainline/)?" + prefix)
Bob Badoure6fdd142021-12-09 22:10:43 -0800119 SafePrebuiltPrefixes[r] = prefix
120 }
Bob Badour113c92b2022-09-23 11:27:24 -0700121 OrderedSafePrebuiltPrefixes = make([]*regexp.Regexp, 0, len(SafePrebuiltPrefixes))
122 for r := range SafePrebuiltPrefixes {
123 OrderedSafePrebuiltPrefixes = append(OrderedSafePrebuiltPrefixes, r)
124 }
125 sort.Sort(byIncreasingLength(OrderedSafePrebuiltPrefixes))
Bob Badoure6fdd142021-12-09 22:10:43 -0800126}
Bob Badour103eb0f2022-01-10 13:50:57 -0800127
128// LicenseConditionSetFromNames returns a set containing the recognized `names` and
129// silently ignoring or discarding the unrecognized `names`.
130func LicenseConditionSetFromNames(tn *TargetNode, names ...string) LicenseConditionSet {
131 cs := NewLicenseConditionSet()
132 for _, name := range names {
133 if name == "restricted" {
134 if 0 == len(tn.LicenseKinds()) {
135 cs = cs.Plus(RestrictedCondition)
136 continue
137 }
138 hasLgpl := false
Bob Badour103eb0f2022-01-10 13:50:57 -0800139 hasGeneric := false
140 for _, kind := range tn.LicenseKinds() {
Bob Badour10f5c482022-09-20 21:44:17 -0700141 if anyLgpl.MatchString(kind) {
Bob Badour103eb0f2022-01-10 13:50:57 -0800142 cs = cs.Plus(WeaklyRestrictedCondition)
143 hasLgpl = true
144 } else if versionedGpl.MatchString(kind) {
145 cs = cs.Plus(RestrictedCondition)
146 } else if genericGpl.MatchString(kind) {
147 hasGeneric = true
148 } else if kind == "legacy_restricted" || ccBySa.MatchString(kind) {
149 cs = cs.Plus(RestrictedCondition)
150 } else {
151 cs = cs.Plus(RestrictedCondition)
152 }
153 }
Bob Badour10f5c482022-09-20 21:44:17 -0700154 if hasGeneric && !hasLgpl {
Bob Badour103eb0f2022-01-10 13:50:57 -0800155 cs = cs.Plus(RestrictedCondition)
156 }
157 continue
158 }
159 if lc, ok := RecognizedConditionNames[name]; ok {
160 cs |= LicenseConditionSet(lc)
161 }
162 }
163 return cs
164}
165
Bob Badour103eb0f2022-01-10 13:50:57 -0800166// Resolution happens in three phases:
Bob Badour9ee7d032021-10-25 16:51:48 -0700167//
Bob Badour103eb0f2022-01-10 13:50:57 -0800168// 1. A bottom-up traversal propagates (restricted) license conditions up to
169// targets from dendencies as needed.
Bob Badour9ee7d032021-10-25 16:51:48 -0700170//
Bob Badour103eb0f2022-01-10 13:50:57 -0800171// 2. For each condition of interest, a top-down traversal propagates
172// (restricted) conditions down from targets into linked dependencies.
Bob Badour9ee7d032021-10-25 16:51:48 -0700173//
Bob Badour103eb0f2022-01-10 13:50:57 -0800174// 3. Finally, a walk of the shipped target nodes attaches resolutions to the
175// ancestor nodes from the root down to and including the first non-container.
Bob Badour9ee7d032021-10-25 16:51:48 -0700176//
Bob Badour103eb0f2022-01-10 13:50:57 -0800177// e.g. If a disk image contains a binary bin1 that links a library liba, the
178// notice requirement for liba gets attached to the disk image and to bin1.
179// Because liba doesn't actually get shipped as a separate artifact, but only
180// as bits in bin1, it has no actions 'attached' to it. The actions attached
181// to the image and to bin1 'act on' liba by providing notice.
Bob Badour9ee7d032021-10-25 16:51:48 -0700182//
Bob Badour103eb0f2022-01-10 13:50:57 -0800183// The behavior of the 3 phases gets controlled by the 3 functions below.
Bob Badour9ee7d032021-10-25 16:51:48 -0700184//
Bob Badour103eb0f2022-01-10 13:50:57 -0800185// The first function controls what happens during the bottom-up propagation.
186// Restricted conditions propagate up all non-toolchain dependencies; except,
187// some do not propagate up dynamic links, which may depend on whether the
188// modules are independent.
189//
190// The second function controls what happens during the top-down propagation.
191// Restricted conditions propagate down as above with the added caveat that
192// inherited restricted conditions do not propagate from pure aggregates to
193// their dependencies.
194//
195// The final function controls which conditions apply/get attached to ancestors
196// depending on the types of dependencies involved. All conditions apply across
197// normal derivation dependencies. No conditions apply across toolchain
198// dependencies. Some restricted conditions apply across dynamic link
199// dependencies.
Bob Badour9ee7d032021-10-25 16:51:48 -0700200//
201// Not all restricted licenses are create equal. Some have special rules or
202// exceptions. e.g. LGPL or "with classpath excption".
203
Bob Badour103eb0f2022-01-10 13:50:57 -0800204// depConditionsPropagatingToTarget returns the conditions which propagate up an
Bob Badour9ee7d032021-10-25 16:51:48 -0700205// edge from dependency to target.
206//
Bob Badour103eb0f2022-01-10 13:50:57 -0800207// This function sets the policy for the bottom-up propagation and how conditions
Bob Badour9ee7d032021-10-25 16:51:48 -0700208// flow up the graph from dependencies to targets.
209//
210// If a pure aggregation is built into a derivative work that is not a pure
211// aggregation, per policy it ceases to be a pure aggregation in the context of
212// that derivative work. The `treatAsAggregate` parameter will be false for
213// non-aggregates and for aggregates in non-aggregate contexts.
Bob Badour103eb0f2022-01-10 13:50:57 -0800214func depConditionsPropagatingToTarget(lg *LicenseGraph, e *TargetEdge, depConditions LicenseConditionSet, treatAsAggregate bool) LicenseConditionSet {
215 result := LicenseConditionSet(0x0000)
Bob Badour9ee7d032021-10-25 16:51:48 -0700216 if edgeIsDerivation(e) {
Bob Badour103eb0f2022-01-10 13:50:57 -0800217 result |= depConditions & ImpliesRestricted
Bob Badour9ee7d032021-10-25 16:51:48 -0700218 return result
219 }
220 if !edgeIsDynamicLink(e) {
221 return result
222 }
223
Bob Badour103eb0f2022-01-10 13:50:57 -0800224 result |= depConditions & LicenseConditionSet(RestrictedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -0700225 return result
226}
227
Bob Badour103eb0f2022-01-10 13:50:57 -0800228// targetConditionsPropagatingToDep returns the conditions which propagate down
Bob Badour9ee7d032021-10-25 16:51:48 -0700229// an edge from target to dependency.
230//
231// This function sets the policy for the top-down traversal and how conditions
232// flow down the graph from targets to dependencies.
233//
234// If a pure aggregation is built into a derivative work that is not a pure
235// aggregation, per policy it ceases to be a pure aggregation in the context of
236// that derivative work. The `treatAsAggregate` parameter will be false for
237// non-aggregates and for aggregates in non-aggregate contexts.
Bob Badourc8178452022-01-31 13:05:53 -0800238func targetConditionsPropagatingToDep(lg *LicenseGraph, e *TargetEdge, targetConditions LicenseConditionSet, treatAsAggregate bool, conditionsFn TraceConditions) LicenseConditionSet {
Bob Badour103eb0f2022-01-10 13:50:57 -0800239 result := targetConditions
Bob Badour9ee7d032021-10-25 16:51:48 -0700240
241 // reverse direction -- none of these apply to things depended-on, only to targets depending-on.
Bob Badour103eb0f2022-01-10 13:50:57 -0800242 result = result.Minus(UnencumberedCondition, PermissiveCondition, NoticeCondition, ReciprocalCondition, ProprietaryCondition, ByExceptionOnlyCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -0700243
244 if !edgeIsDerivation(e) && !edgeIsDynamicLink(e) {
245 // target is not a derivative work of dependency and is not linked to dependency
Bob Badour103eb0f2022-01-10 13:50:57 -0800246 result = result.Difference(ImpliesRestricted)
Bob Badour9ee7d032021-10-25 16:51:48 -0700247 return result
248 }
249 if treatAsAggregate {
250 // If the author of a pure aggregate licenses it restricted, apply restricted to immediate dependencies.
251 // Otherwise, restricted does not propagate back down to dependencies.
Bob Badourc8178452022-01-31 13:05:53 -0800252 if !conditionsFn(e.target).MatchesAnySet(ImpliesRestricted) {
Bob Badour103eb0f2022-01-10 13:50:57 -0800253 result = result.Difference(ImpliesRestricted)
Bob Badour9ee7d032021-10-25 16:51:48 -0700254 }
255 return result
256 }
257 if edgeIsDerivation(e) {
258 return result
259 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800260 result = result.Minus(WeaklyRestrictedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -0700261 return result
262}
263
Bob Badour103eb0f2022-01-10 13:50:57 -0800264// conditionsAttachingAcrossEdge returns the subset of conditions in `universe`
265// that apply across edge `e`.
266//
267// This function sets the policy for attaching actions to ancestor nodes in the
268// final resolution walk.
269func conditionsAttachingAcrossEdge(lg *LicenseGraph, e *TargetEdge, universe LicenseConditionSet) LicenseConditionSet {
270 result := universe
271 if edgeIsDerivation(e) {
272 return result
273 }
274 if !edgeIsDynamicLink(e) {
275 return NewLicenseConditionSet()
276 }
277
Bob Badour10f5c482022-09-20 21:44:17 -0700278 result &= LicenseConditionSet(RestrictedCondition)
Bob Badour103eb0f2022-01-10 13:50:57 -0800279 return result
280}
281
Bob Badour9ee7d032021-10-25 16:51:48 -0700282// edgeIsDynamicLink returns true for edges representing shared libraries
283// linked dynamically at runtime.
Bob Badour103eb0f2022-01-10 13:50:57 -0800284func edgeIsDynamicLink(e *TargetEdge) bool {
285 return e.annotations.HasAnnotation("dynamic")
Bob Badour9ee7d032021-10-25 16:51:48 -0700286}
287
288// edgeIsDerivation returns true for edges where the target is a derivative
289// work of dependency.
Bob Badour103eb0f2022-01-10 13:50:57 -0800290func edgeIsDerivation(e *TargetEdge) bool {
291 isDynamic := e.annotations.HasAnnotation("dynamic")
292 isToolchain := e.annotations.HasAnnotation("toolchain")
Bob Badour9ee7d032021-10-25 16:51:48 -0700293 return !isDynamic && !isToolchain
294}