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