blob: 02d1d967ed2db01ad7e7992f1e15e35260843ae0 [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 "strings"
Bob Badour9ee7d032021-10-25 16:51:48 -070020)
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
Colin Cross4b545252022-09-28 15:36:53 -070032 // safePathPrefixes maps the path prefixes presumed not to contain any
Bob Badoure6fdd142021-12-09 22:10:43 -080033 // proprietary or confidential pathnames to whether to strip the prefix
34 // from the path when used as the library name for notices.
Colin Cross4b545252022-09-28 15:36:53 -070035 safePathPrefixes = []safePathPrefixesType{
36 {"external/", true},
37 {"art/", false},
38 {"build/", false},
39 {"cts/", false},
40 {"dalvik/", false},
41 {"developers/", false},
42 {"development/", false},
43 {"frameworks/", false},
44 {"packages/", true},
45 {"prebuilts/module_sdk/", true},
46 {"prebuilts/", false},
47 {"sdk/", false},
48 {"system/", false},
49 {"test/", false},
50 {"toolchain/", false},
51 {"tools/", false},
Bob Badoure6fdd142021-12-09 22:10:43 -080052 }
53
Colin Cross4b545252022-09-28 15:36:53 -070054 // safePrebuiltPrefixes maps the regular expression to match a prebuilt
Bob Badoure6fdd142021-12-09 22:10:43 -080055 // containing the path of a safe prefix to the safe prefix.
Colin Cross4b545252022-09-28 15:36:53 -070056 safePrebuiltPrefixes []safePrebuiltPrefixesType
Bob Badour113c92b2022-09-23 11:27:24 -070057
Bob Badour9ee7d032021-10-25 16:51:48 -070058 // ImpliesUnencumbered lists the condition names representing an author attempt to disclaim copyright.
Bob Badour103eb0f2022-01-10 13:50:57 -080059 ImpliesUnencumbered = LicenseConditionSet(UnencumberedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070060
61 // ImpliesPermissive lists the condition names representing copyrighted but "licensed without policy requirements".
Bob Badour103eb0f2022-01-10 13:50:57 -080062 ImpliesPermissive = LicenseConditionSet(PermissiveCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070063
64 // ImpliesNotice lists the condition names implying a notice or attribution policy.
Bob Badour103eb0f2022-01-10 13:50:57 -080065 ImpliesNotice = LicenseConditionSet(UnencumberedCondition | PermissiveCondition | NoticeCondition | ReciprocalCondition |
Bob Badour10f5c482022-09-20 21:44:17 -070066 RestrictedCondition | WeaklyRestrictedCondition | 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 Badour10f5c482022-09-20 21:44:17 -070072 ImpliesRestricted = LicenseConditionSet(RestrictedCondition | 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 Badour10f5c482022-09-20 21:44:17 -070084 ImpliesShared = LicenseConditionSet(ReciprocalCondition | RestrictedCondition | WeaklyRestrictedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -070085)
86
Colin Cross4b545252022-09-28 15:36:53 -070087type safePathPrefixesType struct {
88 prefix string
89 strip bool
90}
91
92type safePrebuiltPrefixesType struct {
93 safePathPrefixesType
94 re *regexp.Regexp
95}
96
Bob Badour9ee7d032021-10-25 16:51:48 -070097var (
98 anyLgpl = regexp.MustCompile(`^SPDX-license-identifier-LGPL.*`)
99 versionedGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL-\p{N}.*`)
100 genericGpl = regexp.MustCompile(`^SPDX-license-identifier-GPL$`)
101 ccBySa = regexp.MustCompile(`^SPDX-license-identifier-CC-BY.*-SA.*`)
102)
103
Bob Badoure6fdd142021-12-09 22:10:43 -0800104func init() {
Colin Cross4b545252022-09-28 15:36:53 -0700105 for _, safePathPrefix := range safePathPrefixes {
106 if strings.HasPrefix(safePathPrefix.prefix, "prebuilts/") {
Bob Badoure6fdd142021-12-09 22:10:43 -0800107 continue
108 }
Colin Cross4b545252022-09-28 15:36:53 -0700109 r := regexp.MustCompile("^prebuilts/(?:runtime/mainline/)?" + safePathPrefix.prefix)
110 safePrebuiltPrefixes = append(safePrebuiltPrefixes,
111 safePrebuiltPrefixesType{safePathPrefix, r})
Bob Badoure6fdd142021-12-09 22:10:43 -0800112 }
113}
Bob Badour103eb0f2022-01-10 13:50:57 -0800114
115// LicenseConditionSetFromNames returns a set containing the recognized `names` and
116// silently ignoring or discarding the unrecognized `names`.
117func LicenseConditionSetFromNames(tn *TargetNode, names ...string) LicenseConditionSet {
118 cs := NewLicenseConditionSet()
119 for _, name := range names {
120 if name == "restricted" {
121 if 0 == len(tn.LicenseKinds()) {
122 cs = cs.Plus(RestrictedCondition)
123 continue
124 }
125 hasLgpl := false
Bob Badour103eb0f2022-01-10 13:50:57 -0800126 hasGeneric := false
127 for _, kind := range tn.LicenseKinds() {
Bob Badour10f5c482022-09-20 21:44:17 -0700128 if anyLgpl.MatchString(kind) {
Bob Badour103eb0f2022-01-10 13:50:57 -0800129 cs = cs.Plus(WeaklyRestrictedCondition)
130 hasLgpl = true
131 } else if versionedGpl.MatchString(kind) {
132 cs = cs.Plus(RestrictedCondition)
133 } else if genericGpl.MatchString(kind) {
134 hasGeneric = true
135 } else if kind == "legacy_restricted" || ccBySa.MatchString(kind) {
136 cs = cs.Plus(RestrictedCondition)
137 } else {
138 cs = cs.Plus(RestrictedCondition)
139 }
140 }
Bob Badour10f5c482022-09-20 21:44:17 -0700141 if hasGeneric && !hasLgpl {
Bob Badour103eb0f2022-01-10 13:50:57 -0800142 cs = cs.Plus(RestrictedCondition)
143 }
144 continue
145 }
146 if lc, ok := RecognizedConditionNames[name]; ok {
147 cs |= LicenseConditionSet(lc)
148 }
149 }
150 return cs
151}
152
Bob Badour103eb0f2022-01-10 13:50:57 -0800153// Resolution happens in three phases:
Bob Badour9ee7d032021-10-25 16:51:48 -0700154//
Bob Badour103eb0f2022-01-10 13:50:57 -0800155// 1. A bottom-up traversal propagates (restricted) license conditions up to
156// targets from dendencies as needed.
Bob Badour9ee7d032021-10-25 16:51:48 -0700157//
Bob Badour103eb0f2022-01-10 13:50:57 -0800158// 2. For each condition of interest, a top-down traversal propagates
159// (restricted) conditions down from targets into linked dependencies.
Bob Badour9ee7d032021-10-25 16:51:48 -0700160//
Bob Badour103eb0f2022-01-10 13:50:57 -0800161// 3. Finally, a walk of the shipped target nodes attaches resolutions to the
162// ancestor nodes from the root down to and including the first non-container.
Bob Badour9ee7d032021-10-25 16:51:48 -0700163//
Bob Badour103eb0f2022-01-10 13:50:57 -0800164// e.g. If a disk image contains a binary bin1 that links a library liba, the
165// notice requirement for liba gets attached to the disk image and to bin1.
166// Because liba doesn't actually get shipped as a separate artifact, but only
167// as bits in bin1, it has no actions 'attached' to it. The actions attached
168// to the image and to bin1 'act on' liba by providing notice.
Bob Badour9ee7d032021-10-25 16:51:48 -0700169//
Bob Badour103eb0f2022-01-10 13:50:57 -0800170// The behavior of the 3 phases gets controlled by the 3 functions below.
Bob Badour9ee7d032021-10-25 16:51:48 -0700171//
Bob Badour103eb0f2022-01-10 13:50:57 -0800172// The first function controls what happens during the bottom-up propagation.
173// Restricted conditions propagate up all non-toolchain dependencies; except,
174// some do not propagate up dynamic links, which may depend on whether the
175// modules are independent.
176//
177// The second function controls what happens during the top-down propagation.
178// Restricted conditions propagate down as above with the added caveat that
179// inherited restricted conditions do not propagate from pure aggregates to
180// their dependencies.
181//
182// The final function controls which conditions apply/get attached to ancestors
183// depending on the types of dependencies involved. All conditions apply across
184// normal derivation dependencies. No conditions apply across toolchain
185// dependencies. Some restricted conditions apply across dynamic link
186// dependencies.
Bob Badour9ee7d032021-10-25 16:51:48 -0700187//
188// Not all restricted licenses are create equal. Some have special rules or
189// exceptions. e.g. LGPL or "with classpath excption".
190
Bob Badour103eb0f2022-01-10 13:50:57 -0800191// depConditionsPropagatingToTarget returns the conditions which propagate up an
Bob Badour9ee7d032021-10-25 16:51:48 -0700192// edge from dependency to target.
193//
Bob Badour103eb0f2022-01-10 13:50:57 -0800194// This function sets the policy for the bottom-up propagation and how conditions
Bob Badour9ee7d032021-10-25 16:51:48 -0700195// flow up the graph from dependencies to targets.
196//
197// If a pure aggregation is built into a derivative work that is not a pure
198// aggregation, per policy it ceases to be a pure aggregation in the context of
199// that derivative work. The `treatAsAggregate` parameter will be false for
200// non-aggregates and for aggregates in non-aggregate contexts.
Bob Badour103eb0f2022-01-10 13:50:57 -0800201func depConditionsPropagatingToTarget(lg *LicenseGraph, e *TargetEdge, depConditions LicenseConditionSet, treatAsAggregate bool) LicenseConditionSet {
202 result := LicenseConditionSet(0x0000)
Bob Badour9ee7d032021-10-25 16:51:48 -0700203 if edgeIsDerivation(e) {
Bob Badour103eb0f2022-01-10 13:50:57 -0800204 result |= depConditions & ImpliesRestricted
Bob Badour9ee7d032021-10-25 16:51:48 -0700205 return result
206 }
207 if !edgeIsDynamicLink(e) {
208 return result
209 }
210
Bob Badour103eb0f2022-01-10 13:50:57 -0800211 result |= depConditions & LicenseConditionSet(RestrictedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -0700212 return result
213}
214
Bob Badour103eb0f2022-01-10 13:50:57 -0800215// targetConditionsPropagatingToDep returns the conditions which propagate down
Bob Badour9ee7d032021-10-25 16:51:48 -0700216// an edge from target to dependency.
217//
218// This function sets the policy for the top-down traversal and how conditions
219// flow down the graph from targets to dependencies.
220//
221// If a pure aggregation is built into a derivative work that is not a pure
222// aggregation, per policy it ceases to be a pure aggregation in the context of
223// that derivative work. The `treatAsAggregate` parameter will be false for
224// non-aggregates and for aggregates in non-aggregate contexts.
Bob Badourc8178452022-01-31 13:05:53 -0800225func targetConditionsPropagatingToDep(lg *LicenseGraph, e *TargetEdge, targetConditions LicenseConditionSet, treatAsAggregate bool, conditionsFn TraceConditions) LicenseConditionSet {
Bob Badour103eb0f2022-01-10 13:50:57 -0800226 result := targetConditions
Bob Badour9ee7d032021-10-25 16:51:48 -0700227
228 // reverse direction -- none of these apply to things depended-on, only to targets depending-on.
Bob Badour103eb0f2022-01-10 13:50:57 -0800229 result = result.Minus(UnencumberedCondition, PermissiveCondition, NoticeCondition, ReciprocalCondition, ProprietaryCondition, ByExceptionOnlyCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -0700230
231 if !edgeIsDerivation(e) && !edgeIsDynamicLink(e) {
232 // target is not a derivative work of dependency and is not linked to dependency
Bob Badour103eb0f2022-01-10 13:50:57 -0800233 result = result.Difference(ImpliesRestricted)
Bob Badour9ee7d032021-10-25 16:51:48 -0700234 return result
235 }
236 if treatAsAggregate {
237 // If the author of a pure aggregate licenses it restricted, apply restricted to immediate dependencies.
238 // Otherwise, restricted does not propagate back down to dependencies.
Bob Badourc8178452022-01-31 13:05:53 -0800239 if !conditionsFn(e.target).MatchesAnySet(ImpliesRestricted) {
Bob Badour103eb0f2022-01-10 13:50:57 -0800240 result = result.Difference(ImpliesRestricted)
Bob Badour9ee7d032021-10-25 16:51:48 -0700241 }
242 return result
243 }
244 if edgeIsDerivation(e) {
245 return result
246 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800247 result = result.Minus(WeaklyRestrictedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -0700248 return result
249}
250
Bob Badour103eb0f2022-01-10 13:50:57 -0800251// conditionsAttachingAcrossEdge returns the subset of conditions in `universe`
252// that apply across edge `e`.
253//
254// This function sets the policy for attaching actions to ancestor nodes in the
255// final resolution walk.
256func conditionsAttachingAcrossEdge(lg *LicenseGraph, e *TargetEdge, universe LicenseConditionSet) LicenseConditionSet {
257 result := universe
258 if edgeIsDerivation(e) {
259 return result
260 }
261 if !edgeIsDynamicLink(e) {
262 return NewLicenseConditionSet()
263 }
264
Bob Badour10f5c482022-09-20 21:44:17 -0700265 result &= LicenseConditionSet(RestrictedCondition)
Bob Badour103eb0f2022-01-10 13:50:57 -0800266 return result
267}
268
Bob Badour9ee7d032021-10-25 16:51:48 -0700269// edgeIsDynamicLink returns true for edges representing shared libraries
270// linked dynamically at runtime.
Bob Badour103eb0f2022-01-10 13:50:57 -0800271func edgeIsDynamicLink(e *TargetEdge) bool {
272 return e.annotations.HasAnnotation("dynamic")
Bob Badour9ee7d032021-10-25 16:51:48 -0700273}
274
275// edgeIsDerivation returns true for edges where the target is a derivative
276// work of dependency.
Bob Badour103eb0f2022-01-10 13:50:57 -0800277func edgeIsDerivation(e *TargetEdge) bool {
278 isDynamic := e.annotations.HasAnnotation("dynamic")
279 isToolchain := e.annotations.HasAnnotation("toolchain")
Bob Badour9ee7d032021-10-25 16:51:48 -0700280 return !isDynamic && !isToolchain
281}