blob: 368a1626941f2bae4717ff76f09bfd216d52c1c4 [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`.
Bob Badoura6ee6d52022-12-16 13:50:41 -0800117func LicenseConditionSetFromNames(names ...string) LicenseConditionSet {
Bob Badour103eb0f2022-01-10 13:50:57 -0800118 cs := NewLicenseConditionSet()
119 for _, name := range names {
Bob Badour103eb0f2022-01-10 13:50:57 -0800120 if lc, ok := RecognizedConditionNames[name]; ok {
121 cs |= LicenseConditionSet(lc)
122 }
123 }
124 return cs
125}
126
Bob Badour103eb0f2022-01-10 13:50:57 -0800127// Resolution happens in three phases:
Bob Badour9ee7d032021-10-25 16:51:48 -0700128//
Bob Badour103eb0f2022-01-10 13:50:57 -0800129// 1. A bottom-up traversal propagates (restricted) license conditions up to
130// targets from dendencies as needed.
Bob Badour9ee7d032021-10-25 16:51:48 -0700131//
Bob Badour103eb0f2022-01-10 13:50:57 -0800132// 2. For each condition of interest, a top-down traversal propagates
133// (restricted) conditions down from targets into linked dependencies.
Bob Badour9ee7d032021-10-25 16:51:48 -0700134//
Bob Badour103eb0f2022-01-10 13:50:57 -0800135// 3. Finally, a walk of the shipped target nodes attaches resolutions to the
136// ancestor nodes from the root down to and including the first non-container.
Bob Badour9ee7d032021-10-25 16:51:48 -0700137//
Bob Badour103eb0f2022-01-10 13:50:57 -0800138// e.g. If a disk image contains a binary bin1 that links a library liba, the
139// notice requirement for liba gets attached to the disk image and to bin1.
140// Because liba doesn't actually get shipped as a separate artifact, but only
141// as bits in bin1, it has no actions 'attached' to it. The actions attached
142// to the image and to bin1 'act on' liba by providing notice.
Bob Badour9ee7d032021-10-25 16:51:48 -0700143//
Bob Badour103eb0f2022-01-10 13:50:57 -0800144// The behavior of the 3 phases gets controlled by the 3 functions below.
Bob Badour9ee7d032021-10-25 16:51:48 -0700145//
Bob Badour103eb0f2022-01-10 13:50:57 -0800146// The first function controls what happens during the bottom-up propagation.
147// Restricted conditions propagate up all non-toolchain dependencies; except,
148// some do not propagate up dynamic links, which may depend on whether the
149// modules are independent.
150//
151// The second function controls what happens during the top-down propagation.
152// Restricted conditions propagate down as above with the added caveat that
153// inherited restricted conditions do not propagate from pure aggregates to
154// their dependencies.
155//
156// The final function controls which conditions apply/get attached to ancestors
157// depending on the types of dependencies involved. All conditions apply across
158// normal derivation dependencies. No conditions apply across toolchain
159// dependencies. Some restricted conditions apply across dynamic link
160// dependencies.
Bob Badour9ee7d032021-10-25 16:51:48 -0700161//
162// Not all restricted licenses are create equal. Some have special rules or
163// exceptions. e.g. LGPL or "with classpath excption".
164
Bob Badour103eb0f2022-01-10 13:50:57 -0800165// depConditionsPropagatingToTarget returns the conditions which propagate up an
Bob Badour9ee7d032021-10-25 16:51:48 -0700166// edge from dependency to target.
167//
Bob Badour103eb0f2022-01-10 13:50:57 -0800168// This function sets the policy for the bottom-up propagation and how conditions
Bob Badour9ee7d032021-10-25 16:51:48 -0700169// flow up the graph from dependencies to targets.
170//
171// If a pure aggregation is built into a derivative work that is not a pure
172// aggregation, per policy it ceases to be a pure aggregation in the context of
173// that derivative work. The `treatAsAggregate` parameter will be false for
174// non-aggregates and for aggregates in non-aggregate contexts.
Bob Badour103eb0f2022-01-10 13:50:57 -0800175func depConditionsPropagatingToTarget(lg *LicenseGraph, e *TargetEdge, depConditions LicenseConditionSet, treatAsAggregate bool) LicenseConditionSet {
176 result := LicenseConditionSet(0x0000)
Bob Badour9ee7d032021-10-25 16:51:48 -0700177 if edgeIsDerivation(e) {
Bob Badour103eb0f2022-01-10 13:50:57 -0800178 result |= depConditions & ImpliesRestricted
Bob Badour9ee7d032021-10-25 16:51:48 -0700179 return result
180 }
181 if !edgeIsDynamicLink(e) {
182 return result
183 }
184
Bob Badour103eb0f2022-01-10 13:50:57 -0800185 result |= depConditions & LicenseConditionSet(RestrictedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -0700186 return result
187}
188
Bob Badour103eb0f2022-01-10 13:50:57 -0800189// targetConditionsPropagatingToDep returns the conditions which propagate down
Bob Badour9ee7d032021-10-25 16:51:48 -0700190// an edge from target to dependency.
191//
192// This function sets the policy for the top-down traversal and how conditions
193// flow down the graph from targets to dependencies.
194//
195// If a pure aggregation is built into a derivative work that is not a pure
196// aggregation, per policy it ceases to be a pure aggregation in the context of
197// that derivative work. The `treatAsAggregate` parameter will be false for
198// non-aggregates and for aggregates in non-aggregate contexts.
Bob Badourc8178452022-01-31 13:05:53 -0800199func targetConditionsPropagatingToDep(lg *LicenseGraph, e *TargetEdge, targetConditions LicenseConditionSet, treatAsAggregate bool, conditionsFn TraceConditions) LicenseConditionSet {
Bob Badour103eb0f2022-01-10 13:50:57 -0800200 result := targetConditions
Bob Badour9ee7d032021-10-25 16:51:48 -0700201
202 // reverse direction -- none of these apply to things depended-on, only to targets depending-on.
Bob Badour103eb0f2022-01-10 13:50:57 -0800203 result = result.Minus(UnencumberedCondition, PermissiveCondition, NoticeCondition, ReciprocalCondition, ProprietaryCondition, ByExceptionOnlyCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -0700204
205 if !edgeIsDerivation(e) && !edgeIsDynamicLink(e) {
206 // target is not a derivative work of dependency and is not linked to dependency
Bob Badour103eb0f2022-01-10 13:50:57 -0800207 result = result.Difference(ImpliesRestricted)
Bob Badour9ee7d032021-10-25 16:51:48 -0700208 return result
209 }
210 if treatAsAggregate {
211 // If the author of a pure aggregate licenses it restricted, apply restricted to immediate dependencies.
212 // Otherwise, restricted does not propagate back down to dependencies.
Bob Badourc8178452022-01-31 13:05:53 -0800213 if !conditionsFn(e.target).MatchesAnySet(ImpliesRestricted) {
Bob Badour103eb0f2022-01-10 13:50:57 -0800214 result = result.Difference(ImpliesRestricted)
Bob Badour9ee7d032021-10-25 16:51:48 -0700215 }
216 return result
217 }
218 if edgeIsDerivation(e) {
219 return result
220 }
Bob Badour103eb0f2022-01-10 13:50:57 -0800221 result = result.Minus(WeaklyRestrictedCondition)
Bob Badour9ee7d032021-10-25 16:51:48 -0700222 return result
223}
224
Bob Badour103eb0f2022-01-10 13:50:57 -0800225// conditionsAttachingAcrossEdge returns the subset of conditions in `universe`
226// that apply across edge `e`.
227//
228// This function sets the policy for attaching actions to ancestor nodes in the
229// final resolution walk.
230func conditionsAttachingAcrossEdge(lg *LicenseGraph, e *TargetEdge, universe LicenseConditionSet) LicenseConditionSet {
231 result := universe
232 if edgeIsDerivation(e) {
233 return result
234 }
235 if !edgeIsDynamicLink(e) {
236 return NewLicenseConditionSet()
237 }
238
Bob Badour10f5c482022-09-20 21:44:17 -0700239 result &= LicenseConditionSet(RestrictedCondition)
Bob Badour103eb0f2022-01-10 13:50:57 -0800240 return result
241}
242
Bob Badour9ee7d032021-10-25 16:51:48 -0700243// edgeIsDynamicLink returns true for edges representing shared libraries
244// linked dynamically at runtime.
Bob Badour103eb0f2022-01-10 13:50:57 -0800245func edgeIsDynamicLink(e *TargetEdge) bool {
246 return e.annotations.HasAnnotation("dynamic")
Bob Badour9ee7d032021-10-25 16:51:48 -0700247}
248
249// edgeIsDerivation returns true for edges where the target is a derivative
250// work of dependency.
Bob Badour103eb0f2022-01-10 13:50:57 -0800251func edgeIsDerivation(e *TargetEdge) bool {
252 isDynamic := e.annotations.HasAnnotation("dynamic")
253 isToolchain := e.annotations.HasAnnotation("toolchain")
Bob Badour9ee7d032021-10-25 16:51:48 -0700254 return !isDynamic && !isToolchain
255}