Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 1 | // 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 | |
| 15 | package compliance |
| 16 | |
| 17 | import ( |
| 18 | "regexp" |
Bob Badour | 113c92b | 2022-09-23 11:27:24 -0700 | [diff] [blame^] | 19 | "sort" |
| 20 | "strings" |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 21 | ) |
| 22 | |
| 23 | var ( |
Bob Badour | 67d8ae3 | 2022-01-10 18:32:54 -0800 | [diff] [blame] | 24 | // 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 Badour | e6fdd14 | 2021-12-09 22:10:43 -0800 | [diff] [blame] | 33 | // 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 Badour | 113c92b | 2022-09-23 11:27:24 -0700 | [diff] [blame^] | 37 | "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 Badour | e6fdd14 | 2021-12-09 22:10:43 -0800 | [diff] [blame] | 53 | } |
| 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 Badour | 113c92b | 2022-09-23 11:27:24 -0700 | [diff] [blame^] | 59 | // OrderedSafePrebuiltPrefixes lists the SafePrebuiltPrefixes ordered by |
| 60 | // increasing length. |
| 61 | OrderedSafePrebuiltPrefixes = make([]*regexp.Regexp, 0, 0) |
| 62 | |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 63 | // ImpliesUnencumbered lists the condition names representing an author attempt to disclaim copyright. |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 64 | ImpliesUnencumbered = LicenseConditionSet(UnencumberedCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 65 | |
| 66 | // ImpliesPermissive lists the condition names representing copyrighted but "licensed without policy requirements". |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 67 | ImpliesPermissive = LicenseConditionSet(PermissiveCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 68 | |
| 69 | // ImpliesNotice lists the condition names implying a notice or attribution policy. |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 70 | ImpliesNotice = LicenseConditionSet(UnencumberedCondition | PermissiveCondition | NoticeCondition | ReciprocalCondition | |
Bob Badour | 10f5c48 | 2022-09-20 21:44:17 -0700 | [diff] [blame] | 71 | RestrictedCondition | WeaklyRestrictedCondition | ProprietaryCondition | ByExceptionOnlyCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 72 | |
| 73 | // ImpliesReciprocal lists the condition names implying a local source-sharing policy. |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 74 | ImpliesReciprocal = LicenseConditionSet(ReciprocalCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 75 | |
| 76 | // Restricted lists the condition names implying an infectious source-sharing policy. |
Bob Badour | 10f5c48 | 2022-09-20 21:44:17 -0700 | [diff] [blame] | 77 | ImpliesRestricted = LicenseConditionSet(RestrictedCondition | WeaklyRestrictedCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 78 | |
| 79 | // ImpliesProprietary lists the condition names implying a confidentiality policy. |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 80 | ImpliesProprietary = LicenseConditionSet(ProprietaryCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 81 | |
| 82 | // ImpliesByExceptionOnly lists the condition names implying a policy for "license review and approval before use". |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 83 | ImpliesByExceptionOnly = LicenseConditionSet(ProprietaryCondition | ByExceptionOnlyCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 84 | |
| 85 | // ImpliesPrivate lists the condition names implying a source-code privacy policy. |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 86 | ImpliesPrivate = LicenseConditionSet(ProprietaryCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 87 | |
| 88 | // ImpliesShared lists the condition names implying a source-code sharing policy. |
Bob Badour | 10f5c48 | 2022-09-20 21:44:17 -0700 | [diff] [blame] | 89 | ImpliesShared = LicenseConditionSet(ReciprocalCondition | RestrictedCondition | WeaklyRestrictedCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 90 | ) |
| 91 | |
| 92 | var ( |
| 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 Badour | 113c92b | 2022-09-23 11:27:24 -0700 | [diff] [blame^] | 99 | // byIncreasingLength implements `sort.Interface` to order regular expressions by increasing length. |
| 100 | type byIncreasingLength []*regexp.Regexp |
| 101 | |
| 102 | func (l byIncreasingLength) Len() int { return len(l) } |
| 103 | func (l byIncreasingLength) Swap(i, j int) { l[i], l[j] = l[j], l[i] } |
| 104 | func (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 Badour | e6fdd14 | 2021-12-09 22:10:43 -0800 | [diff] [blame] | 113 | func init() { |
| 114 | for prefix := range SafePathPrefixes { |
Bob Badour | 113c92b | 2022-09-23 11:27:24 -0700 | [diff] [blame^] | 115 | if strings.HasPrefix(prefix, "prebuilts/") { |
Bob Badour | e6fdd14 | 2021-12-09 22:10:43 -0800 | [diff] [blame] | 116 | continue |
| 117 | } |
Bob Badour | 113c92b | 2022-09-23 11:27:24 -0700 | [diff] [blame^] | 118 | r := regexp.MustCompile("^prebuilts/(?:runtime/mainline/)?" + prefix) |
Bob Badour | e6fdd14 | 2021-12-09 22:10:43 -0800 | [diff] [blame] | 119 | SafePrebuiltPrefixes[r] = prefix |
| 120 | } |
Bob Badour | 113c92b | 2022-09-23 11:27:24 -0700 | [diff] [blame^] | 121 | OrderedSafePrebuiltPrefixes = make([]*regexp.Regexp, 0, len(SafePrebuiltPrefixes)) |
| 122 | for r := range SafePrebuiltPrefixes { |
| 123 | OrderedSafePrebuiltPrefixes = append(OrderedSafePrebuiltPrefixes, r) |
| 124 | } |
| 125 | sort.Sort(byIncreasingLength(OrderedSafePrebuiltPrefixes)) |
Bob Badour | e6fdd14 | 2021-12-09 22:10:43 -0800 | [diff] [blame] | 126 | } |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 127 | |
| 128 | // LicenseConditionSetFromNames returns a set containing the recognized `names` and |
| 129 | // silently ignoring or discarding the unrecognized `names`. |
| 130 | func 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 Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 139 | hasGeneric := false |
| 140 | for _, kind := range tn.LicenseKinds() { |
Bob Badour | 10f5c48 | 2022-09-20 21:44:17 -0700 | [diff] [blame] | 141 | if anyLgpl.MatchString(kind) { |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 142 | 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 Badour | 10f5c48 | 2022-09-20 21:44:17 -0700 | [diff] [blame] | 154 | if hasGeneric && !hasLgpl { |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 155 | 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 Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 166 | // Resolution happens in three phases: |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 167 | // |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 168 | // 1. A bottom-up traversal propagates (restricted) license conditions up to |
| 169 | // targets from dendencies as needed. |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 170 | // |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 171 | // 2. For each condition of interest, a top-down traversal propagates |
| 172 | // (restricted) conditions down from targets into linked dependencies. |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 173 | // |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 174 | // 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 Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 176 | // |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 177 | // 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 Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 182 | // |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 183 | // The behavior of the 3 phases gets controlled by the 3 functions below. |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 184 | // |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 185 | // 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 Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 200 | // |
| 201 | // Not all restricted licenses are create equal. Some have special rules or |
| 202 | // exceptions. e.g. LGPL or "with classpath excption". |
| 203 | |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 204 | // depConditionsPropagatingToTarget returns the conditions which propagate up an |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 205 | // edge from dependency to target. |
| 206 | // |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 207 | // This function sets the policy for the bottom-up propagation and how conditions |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 208 | // 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 Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 214 | func depConditionsPropagatingToTarget(lg *LicenseGraph, e *TargetEdge, depConditions LicenseConditionSet, treatAsAggregate bool) LicenseConditionSet { |
| 215 | result := LicenseConditionSet(0x0000) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 216 | if edgeIsDerivation(e) { |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 217 | result |= depConditions & ImpliesRestricted |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 218 | return result |
| 219 | } |
| 220 | if !edgeIsDynamicLink(e) { |
| 221 | return result |
| 222 | } |
| 223 | |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 224 | result |= depConditions & LicenseConditionSet(RestrictedCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 225 | return result |
| 226 | } |
| 227 | |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 228 | // targetConditionsPropagatingToDep returns the conditions which propagate down |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 229 | // 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 Badour | c817845 | 2022-01-31 13:05:53 -0800 | [diff] [blame] | 238 | func targetConditionsPropagatingToDep(lg *LicenseGraph, e *TargetEdge, targetConditions LicenseConditionSet, treatAsAggregate bool, conditionsFn TraceConditions) LicenseConditionSet { |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 239 | result := targetConditions |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 240 | |
| 241 | // reverse direction -- none of these apply to things depended-on, only to targets depending-on. |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 242 | result = result.Minus(UnencumberedCondition, PermissiveCondition, NoticeCondition, ReciprocalCondition, ProprietaryCondition, ByExceptionOnlyCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 243 | |
| 244 | if !edgeIsDerivation(e) && !edgeIsDynamicLink(e) { |
| 245 | // target is not a derivative work of dependency and is not linked to dependency |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 246 | result = result.Difference(ImpliesRestricted) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 247 | 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 Badour | c817845 | 2022-01-31 13:05:53 -0800 | [diff] [blame] | 252 | if !conditionsFn(e.target).MatchesAnySet(ImpliesRestricted) { |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 253 | result = result.Difference(ImpliesRestricted) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 254 | } |
| 255 | return result |
| 256 | } |
| 257 | if edgeIsDerivation(e) { |
| 258 | return result |
| 259 | } |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 260 | result = result.Minus(WeaklyRestrictedCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 261 | return result |
| 262 | } |
| 263 | |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 264 | // 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. |
| 269 | func 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 Badour | 10f5c48 | 2022-09-20 21:44:17 -0700 | [diff] [blame] | 278 | result &= LicenseConditionSet(RestrictedCondition) |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 279 | return result |
| 280 | } |
| 281 | |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 282 | // edgeIsDynamicLink returns true for edges representing shared libraries |
| 283 | // linked dynamically at runtime. |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 284 | func edgeIsDynamicLink(e *TargetEdge) bool { |
| 285 | return e.annotations.HasAnnotation("dynamic") |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 286 | } |
| 287 | |
| 288 | // edgeIsDerivation returns true for edges where the target is a derivative |
| 289 | // work of dependency. |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 290 | func edgeIsDerivation(e *TargetEdge) bool { |
| 291 | isDynamic := e.annotations.HasAnnotation("dynamic") |
| 292 | isToolchain := e.annotations.HasAnnotation("toolchain") |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 293 | return !isDynamic && !isToolchain |
| 294 | } |