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 | "strings" |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 20 | ) |
| 21 | |
| 22 | var ( |
Bob Badour | 67d8ae3 | 2022-01-10 18:32:54 -0800 | [diff] [blame] | 23 | // 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 Cross | 4b54525 | 2022-09-28 15:36:53 -0700 | [diff] [blame] | 32 | // safePathPrefixes maps the path prefixes presumed not to contain any |
Bob Badour | e6fdd14 | 2021-12-09 22:10:43 -0800 | [diff] [blame] | 33 | // proprietary or confidential pathnames to whether to strip the prefix |
| 34 | // from the path when used as the library name for notices. |
Colin Cross | 4b54525 | 2022-09-28 15:36:53 -0700 | [diff] [blame] | 35 | 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 Badour | e6fdd14 | 2021-12-09 22:10:43 -0800 | [diff] [blame] | 52 | } |
| 53 | |
Colin Cross | 4b54525 | 2022-09-28 15:36:53 -0700 | [diff] [blame] | 54 | // safePrebuiltPrefixes maps the regular expression to match a prebuilt |
Bob Badour | e6fdd14 | 2021-12-09 22:10:43 -0800 | [diff] [blame] | 55 | // containing the path of a safe prefix to the safe prefix. |
Colin Cross | 4b54525 | 2022-09-28 15:36:53 -0700 | [diff] [blame] | 56 | safePrebuiltPrefixes []safePrebuiltPrefixesType |
Bob Badour | 113c92b | 2022-09-23 11:27:24 -0700 | [diff] [blame] | 57 | |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 58 | // ImpliesUnencumbered lists the condition names representing an author attempt to disclaim copyright. |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 59 | ImpliesUnencumbered = LicenseConditionSet(UnencumberedCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 60 | |
| 61 | // ImpliesPermissive lists the condition names representing copyrighted but "licensed without policy requirements". |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 62 | ImpliesPermissive = LicenseConditionSet(PermissiveCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 63 | |
| 64 | // ImpliesNotice lists the condition names implying a notice or attribution policy. |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 65 | ImpliesNotice = LicenseConditionSet(UnencumberedCondition | PermissiveCondition | NoticeCondition | ReciprocalCondition | |
Bob Badour | 10f5c48 | 2022-09-20 21:44:17 -0700 | [diff] [blame] | 66 | RestrictedCondition | WeaklyRestrictedCondition | ProprietaryCondition | ByExceptionOnlyCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 67 | |
| 68 | // ImpliesReciprocal lists the condition names implying a local source-sharing policy. |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 69 | ImpliesReciprocal = LicenseConditionSet(ReciprocalCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 70 | |
| 71 | // Restricted lists the condition names implying an infectious source-sharing policy. |
Bob Badour | 10f5c48 | 2022-09-20 21:44:17 -0700 | [diff] [blame] | 72 | ImpliesRestricted = LicenseConditionSet(RestrictedCondition | WeaklyRestrictedCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 73 | |
| 74 | // ImpliesProprietary lists the condition names implying a confidentiality policy. |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 75 | ImpliesProprietary = LicenseConditionSet(ProprietaryCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 76 | |
| 77 | // 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] | 78 | ImpliesByExceptionOnly = LicenseConditionSet(ProprietaryCondition | ByExceptionOnlyCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 79 | |
| 80 | // ImpliesPrivate lists the condition names implying a source-code privacy policy. |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 81 | ImpliesPrivate = LicenseConditionSet(ProprietaryCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 82 | |
| 83 | // ImpliesShared lists the condition names implying a source-code sharing policy. |
Bob Badour | 10f5c48 | 2022-09-20 21:44:17 -0700 | [diff] [blame] | 84 | ImpliesShared = LicenseConditionSet(ReciprocalCondition | RestrictedCondition | WeaklyRestrictedCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 85 | ) |
| 86 | |
Colin Cross | 4b54525 | 2022-09-28 15:36:53 -0700 | [diff] [blame] | 87 | type safePathPrefixesType struct { |
| 88 | prefix string |
| 89 | strip bool |
| 90 | } |
| 91 | |
| 92 | type safePrebuiltPrefixesType struct { |
| 93 | safePathPrefixesType |
| 94 | re *regexp.Regexp |
| 95 | } |
| 96 | |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 97 | var ( |
| 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 Badour | e6fdd14 | 2021-12-09 22:10:43 -0800 | [diff] [blame] | 104 | func init() { |
Colin Cross | 4b54525 | 2022-09-28 15:36:53 -0700 | [diff] [blame] | 105 | for _, safePathPrefix := range safePathPrefixes { |
| 106 | if strings.HasPrefix(safePathPrefix.prefix, "prebuilts/") { |
Bob Badour | e6fdd14 | 2021-12-09 22:10:43 -0800 | [diff] [blame] | 107 | continue |
| 108 | } |
Colin Cross | 4b54525 | 2022-09-28 15:36:53 -0700 | [diff] [blame] | 109 | r := regexp.MustCompile("^prebuilts/(?:runtime/mainline/)?" + safePathPrefix.prefix) |
| 110 | safePrebuiltPrefixes = append(safePrebuiltPrefixes, |
| 111 | safePrebuiltPrefixesType{safePathPrefix, r}) |
Bob Badour | e6fdd14 | 2021-12-09 22:10:43 -0800 | [diff] [blame] | 112 | } |
| 113 | } |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 114 | |
| 115 | // LicenseConditionSetFromNames returns a set containing the recognized `names` and |
| 116 | // silently ignoring or discarding the unrecognized `names`. |
Bob Badour | a6ee6d5 | 2022-12-16 13:50:41 -0800 | [diff] [blame] | 117 | func LicenseConditionSetFromNames(names ...string) LicenseConditionSet { |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 118 | cs := NewLicenseConditionSet() |
| 119 | for _, name := range names { |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 120 | if lc, ok := RecognizedConditionNames[name]; ok { |
| 121 | cs |= LicenseConditionSet(lc) |
| 122 | } |
| 123 | } |
| 124 | return cs |
| 125 | } |
| 126 | |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 127 | // Resolution happens in three phases: |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 128 | // |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 129 | // 1. A bottom-up traversal propagates (restricted) license conditions up to |
| 130 | // targets from dendencies as needed. |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 131 | // |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 132 | // 2. For each condition of interest, a top-down traversal propagates |
| 133 | // (restricted) conditions down from targets into linked dependencies. |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 134 | // |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 135 | // 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 Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 137 | // |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 138 | // 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 Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 143 | // |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 144 | // 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] | 145 | // |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 146 | // 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 Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 161 | // |
| 162 | // Not all restricted licenses are create equal. Some have special rules or |
| 163 | // exceptions. e.g. LGPL or "with classpath excption". |
| 164 | |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 165 | // depConditionsPropagatingToTarget returns the conditions which propagate up an |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 166 | // edge from dependency to target. |
| 167 | // |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 168 | // 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] | 169 | // 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 Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 175 | func depConditionsPropagatingToTarget(lg *LicenseGraph, e *TargetEdge, depConditions LicenseConditionSet, treatAsAggregate bool) LicenseConditionSet { |
| 176 | result := LicenseConditionSet(0x0000) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 177 | if edgeIsDerivation(e) { |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 178 | result |= depConditions & ImpliesRestricted |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 179 | return result |
| 180 | } |
| 181 | if !edgeIsDynamicLink(e) { |
| 182 | return result |
| 183 | } |
| 184 | |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 185 | result |= depConditions & LicenseConditionSet(RestrictedCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 186 | return result |
| 187 | } |
| 188 | |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 189 | // targetConditionsPropagatingToDep returns the conditions which propagate down |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 190 | // 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 Badour | c817845 | 2022-01-31 13:05:53 -0800 | [diff] [blame] | 199 | 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] | 200 | result := targetConditions |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 201 | |
| 202 | // 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] | 203 | result = result.Minus(UnencumberedCondition, PermissiveCondition, NoticeCondition, ReciprocalCondition, ProprietaryCondition, ByExceptionOnlyCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 204 | |
| 205 | if !edgeIsDerivation(e) && !edgeIsDynamicLink(e) { |
| 206 | // 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] | 207 | result = result.Difference(ImpliesRestricted) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 208 | 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 Badour | c817845 | 2022-01-31 13:05:53 -0800 | [diff] [blame] | 213 | if !conditionsFn(e.target).MatchesAnySet(ImpliesRestricted) { |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 214 | result = result.Difference(ImpliesRestricted) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 215 | } |
| 216 | return result |
| 217 | } |
| 218 | if edgeIsDerivation(e) { |
| 219 | return result |
| 220 | } |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 221 | result = result.Minus(WeaklyRestrictedCondition) |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 222 | return result |
| 223 | } |
| 224 | |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 225 | // 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. |
| 230 | func 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 Badour | 10f5c48 | 2022-09-20 21:44:17 -0700 | [diff] [blame] | 239 | result &= LicenseConditionSet(RestrictedCondition) |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 240 | return result |
| 241 | } |
| 242 | |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 243 | // edgeIsDynamicLink returns true for edges representing shared libraries |
| 244 | // linked dynamically at runtime. |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 245 | func edgeIsDynamicLink(e *TargetEdge) bool { |
| 246 | return e.annotations.HasAnnotation("dynamic") |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 247 | } |
| 248 | |
| 249 | // edgeIsDerivation returns true for edges where the target is a derivative |
| 250 | // work of dependency. |
Bob Badour | 103eb0f | 2022-01-10 13:50:57 -0800 | [diff] [blame] | 251 | func edgeIsDerivation(e *TargetEdge) bool { |
| 252 | isDynamic := e.annotations.HasAnnotation("dynamic") |
| 253 | isToolchain := e.annotations.HasAnnotation("toolchain") |
Bob Badour | 9ee7d03 | 2021-10-25 16:51:48 -0700 | [diff] [blame] | 254 | return !isDynamic && !isToolchain |
| 255 | } |