From df3e90af8f70c5ee11aa984ddade806036fc63c3 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Fri, 5 Jan 2024 00:20:55 +0600 Subject: [PATCH] feat(python): parse licenses from dist-info folder (#4724) Signed-off-by: knqyf263 Co-authored-by: knqyf263 --- .../language/python/packaging/packaging.go | 172 +++++++++--- .../python/packaging/packaging_test.go | 89 +++--- .../setuptools-51.3.3.dist-info}/METADATA | 0 .../setuptools-51.3.3.egg-info}/PKG-INFO | 0 .../{ => egg-zip}/kitchen-1.2.6-py2.7.egg | Bin .../distlib-0.3.1.dist-info}/METADATA | 0 .../distlib-0.3.1.egg-info}/PKG-INFO | 0 .../LICENSE.txt | 254 ++++++++++++++++++ .../METADATA | 189 +++++++++++++ .../{ => no-req-files}/no-required-files.egg | Bin 10 files changed, 636 insertions(+), 68 deletions(-) rename pkg/fanal/analyzer/language/python/packaging/testdata/{classifier-license.dist-info => classifier-license-dist/setuptools-51.3.3.dist-info}/METADATA (100%) rename pkg/fanal/analyzer/language/python/packaging/testdata/{classifier-license.egg-info => classifier-license-egg/setuptools-51.3.3.egg-info}/PKG-INFO (100%) rename pkg/fanal/analyzer/language/python/packaging/testdata/{ => egg-zip}/kitchen-1.2.6-py2.7.egg (100%) rename pkg/fanal/analyzer/language/python/packaging/testdata/{happy.dist-info => happy-dist/distlib-0.3.1.dist-info}/METADATA (100%) rename pkg/fanal/analyzer/language/python/packaging/testdata/{happy.egg-info => happy-egg/distlib-0.3.1.egg-info}/PKG-INFO (100%) create mode 100644 pkg/fanal/analyzer/language/python/packaging/testdata/license-file-dist/typing_extensions-4.4.0.dist-info/LICENSE.txt create mode 100644 pkg/fanal/analyzer/language/python/packaging/testdata/license-file-dist/typing_extensions-4.4.0.dist-info/METADATA rename pkg/fanal/analyzer/language/python/packaging/testdata/{ => no-req-files}/no-required-files.egg (100%) diff --git a/pkg/fanal/analyzer/language/python/packaging/packaging.go b/pkg/fanal/analyzer/language/python/packaging/packaging.go index 790b8e16d25b..9cd03cf0f9a6 100644 --- a/pkg/fanal/analyzer/language/python/packaging/packaging.go +++ b/pkg/fanal/analyzer/language/python/packaging/packaging.go @@ -4,27 +4,43 @@ import ( "archive/zip" "bytes" "context" + "errors" "io" + "io/fs" "os" + "path" + "path/filepath" "strings" + "github.com/samber/lo" "golang.org/x/xerrors" dio "github.com/aquasecurity/go-dep-parser/pkg/io" "github.com/aquasecurity/go-dep-parser/pkg/python/packaging" + godeptypes "github.com/aquasecurity/go-dep-parser/pkg/types" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language" "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/licensing" + "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/utils/fsutils" ) func init() { - analyzer.RegisterAnalyzer(&packagingAnalyzer{}) + analyzer.RegisterPostAnalyzer(analyzer.TypePythonPkg, newPackagingAnalyzer) } const version = 1 +func newPackagingAnalyzer(opt analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) { + return &packagingAnalyzer{ + pkgParser: packaging.NewParser(), + licenseClassifierConfidenceLevel: opt.LicenseScannerOption.ClassifierConfidenceLevel, + }, nil +} + var ( - requiredFiles = []string{ + eggFiles = []string{ // .egg format // https://setuptools.readthedocs.io/en/latest/deprecated/python_eggs.html#eggs-and-their-formats ".egg", // zip format @@ -34,35 +50,125 @@ var ( // https://setuptools.readthedocs.io/en/latest/deprecated/python_eggs.html#eggs-and-their-formats ".egg-info", ".egg-info/PKG-INFO", - - // wheel - ".dist-info/METADATA", } ) -type packagingAnalyzer struct{} +type packagingAnalyzer struct { + pkgParser godeptypes.Parser + licenseClassifierConfidenceLevel float64 +} + +// PostAnalyze analyzes egg and wheel files. +func (a packagingAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { -// Analyze analyzes egg and wheel files. -func (a packagingAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) { - r := input.Content + var apps []types.Application + + required := func(path string, _ fs.DirEntry) bool { + return filepath.Base(path) == "METADATA" || isEggFile(path) + } + + err := fsutils.WalkDir(input.FS, ".", required, func(path string, d fs.DirEntry, r io.Reader) error { + rsa, ok := r.(dio.ReadSeekerAt) + if !ok { + return xerrors.New("invalid reader") + } - // .egg file is zip format and PKG-INFO needs to be extracted from the zip file. - if strings.HasSuffix(input.FilePath, ".egg") { - pkginfoInZip, err := a.analyzeEggZip(input.Content, input.Info.Size()) + // .egg file is zip format and PKG-INFO needs to be extracted from the zip file. + if strings.HasSuffix(path, ".egg") { + info, err := d.Info() + if err != nil { + return xerrors.Errorf("egg file error: %w", err) + } + pkginfoInZip, err := a.analyzeEggZip(rsa, info.Size()) + if err != nil { + return xerrors.Errorf("egg analysis error: %w", err) + } + + // Egg archive may not contain required files, then we will get nil. Skip this archives + if pkginfoInZip == nil { + return nil + } + rsa = pkginfoInZip + } + + app, err := a.parse(path, rsa, input.Options.FileChecksum) if err != nil { - return nil, xerrors.Errorf("egg analysis error: %w", err) + return xerrors.Errorf("parse error: %w", err) + } else if app == nil { + return nil + } + + if err := a.fillAdditionalData(input.FS, app); err != nil { + log.Logger.Warnf("Unable to collect additional info: %s", err) } - // Egg archive may not contain required files, then we will get nil. Skip this archives - if pkginfoInZip == nil { - return nil, nil + apps = append(apps, *app) + return nil + }) + + if err != nil { + return nil, xerrors.Errorf("python package walk error: %w", err) + } + return &analyzer.AnalysisResult{ + Applications: apps, + }, nil +} + +func (a packagingAnalyzer) fillAdditionalData(fsys fs.FS, app *types.Application) error { + for i, lib := range app.Libraries { + var licenses []string + for _, lic := range lib.Licenses { + // Parser adds `file://` prefix to filepath from `License-File` field + // We need to read this file to find licenses + // Otherwise, this is the name of the license + if !strings.HasPrefix(lic, "file://") { + licenses = append(licenses, lic) + continue + } + licenseFilePath := path.Base(strings.TrimPrefix(lic, "file://")) + + findings, err := classifyLicense(app.FilePath, licenseFilePath, a.licenseClassifierConfidenceLevel, fsys) + if err != nil { + return err + } else if len(findings) == 0 { + continue + } + + // License found + foundLicenses := lo.Map(findings, func(finding types.LicenseFinding, _ int) string { + return finding.Name + }) + licenses = append(licenses, foundLicenses...) } + app.Libraries[i].Licenses = licenses + } - r = pkginfoInZip + return nil +} + +func classifyLicense(dir, licPath string, classifierConfidenceLevel float64, fsys fs.FS) (types.LicenseFindings, error) { + // Note that fs.FS is always slashed regardless of the platform, + // and path.Join should be used rather than filepath.Join. + f, err := fsys.Open(path.Join(path.Dir(dir), licPath)) + if errors.Is(err, fs.ErrNotExist) { + return nil, nil + } else if err != nil { + return nil, xerrors.Errorf("file open error: %w", err) } + defer f.Close() - p := packaging.NewParser() - return language.AnalyzePackage(types.PythonPkg, input.FilePath, r, p, input.Options.FileChecksum) + l, err := licensing.Classify(licPath, f, classifierConfidenceLevel) + if err != nil { + return nil, xerrors.Errorf("license classify error: %w", err) + } else if l == nil { + return nil, nil + } + + return l.Findings, nil +} + +func (a packagingAnalyzer) parse(filePath string, r dio.ReadSeekerAt, checksum bool) (*types.Application, error) { + return language.ParsePackage(types.PythonPkg, filePath, r, a.pkgParser, checksum) } func (a packagingAnalyzer) analyzeEggZip(r io.ReaderAt, size int64) (dio.ReadSeekerAt, error) { @@ -71,17 +177,16 @@ func (a packagingAnalyzer) analyzeEggZip(r io.ReaderAt, size int64) (dio.ReadSee return nil, xerrors.Errorf("zip reader error: %w", err) } - for _, file := range zr.File { - if !a.Required(file.Name, nil) { - continue - } - - return a.open(file) + found, ok := lo.Find(zr.File, func(f *zip.File) bool { + return isEggFile(f.Name) + }) + if !ok { + return nil, nil } - - return nil, nil + return a.open(found) } +// open reads the file content in the zip archive to make it seekable. func (a packagingAnalyzer) open(file *zip.File) (dio.ReadSeekerAt, error) { f, err := file.Open() if err != nil { @@ -98,12 +203,13 @@ func (a packagingAnalyzer) open(file *zip.File) (dio.ReadSeekerAt, error) { } func (a packagingAnalyzer) Required(filePath string, _ os.FileInfo) bool { - for _, r := range requiredFiles { - if strings.HasSuffix(filePath, r) { - return true - } - } - return false + return strings.Contains(filePath, ".dist-info") || isEggFile(filePath) +} + +func isEggFile(filePath string) bool { + return lo.SomeBy(eggFiles, func(fileName string) bool { + return strings.HasSuffix(filePath, fileName) + }) } func (a packagingAnalyzer) Type() analyzer.Type { diff --git a/pkg/fanal/analyzer/language/python/packaging/packaging_test.go b/pkg/fanal/analyzer/language/python/packaging/packaging_test.go index 198c0bec273c..420cb7c3bc44 100644 --- a/pkg/fanal/analyzer/language/python/packaging/packaging_test.go +++ b/pkg/fanal/analyzer/language/python/packaging/packaging_test.go @@ -15,25 +15,25 @@ import ( func Test_packagingAnalyzer_Analyze(t *testing.T) { tests := []struct { name string - inputFile string + dir string includeChecksum bool want *analyzer.AnalysisResult wantErr string }{ { - name: "egg zip", - inputFile: "testdata/kitchen-1.2.6-py2.7.egg", + name: "egg zip", + dir: "testdata/egg-zip", want: &analyzer.AnalysisResult{ Applications: []types.Application{ { Type: types.PythonPkg, - FilePath: "testdata/kitchen-1.2.6-py2.7.egg", + FilePath: "kitchen-1.2.6-py2.7.egg", Libraries: types.Packages{ { Name: "kitchen", Version: "1.2.6", Licenses: []string{"LGPLv2+"}, - FilePath: "testdata/kitchen-1.2.6-py2.7.egg", + FilePath: "kitchen-1.2.6-py2.7.egg", }, }, }, @@ -42,19 +42,19 @@ func Test_packagingAnalyzer_Analyze(t *testing.T) { }, { name: "egg-info", - inputFile: "testdata/happy.egg-info/PKG-INFO", + dir: "testdata/happy-egg", includeChecksum: true, want: &analyzer.AnalysisResult{ Applications: []types.Application{ { Type: types.PythonPkg, - FilePath: "testdata/happy.egg-info/PKG-INFO", + FilePath: "distlib-0.3.1.egg-info/PKG-INFO", Libraries: types.Packages{ { Name: "distlib", Version: "0.3.1", Licenses: []string{"Python license"}, - FilePath: "testdata/happy.egg-info/PKG-INFO", + FilePath: "distlib-0.3.1.egg-info/PKG-INFO", Digest: "sha1:d9d89d8ed3b2b683767c96814c9c5d3e57ef2e1b", }, }, @@ -63,19 +63,19 @@ func Test_packagingAnalyzer_Analyze(t *testing.T) { }, }, { - name: "egg-info license classifiers", - inputFile: "testdata/classifier-license.egg-info/PKG-INFO", + name: "egg-info license classifiers", + dir: "testdata/classifier-license-egg", want: &analyzer.AnalysisResult{ Applications: []types.Application{ { Type: types.PythonPkg, - FilePath: "testdata/classifier-license.egg-info/PKG-INFO", + FilePath: "setuptools-51.3.3.egg-info/PKG-INFO", Libraries: types.Packages{ { Name: "setuptools", Version: "51.3.3", Licenses: []string{"MIT License"}, - FilePath: "testdata/classifier-license.egg-info/PKG-INFO", + FilePath: "setuptools-51.3.3.egg-info/PKG-INFO", }, }, }, @@ -83,19 +83,19 @@ func Test_packagingAnalyzer_Analyze(t *testing.T) { }, }, { - name: "dist-info license classifiers", - inputFile: "testdata/classifier-license.dist-info/METADATA", + name: "dist-info license classifiers", + dir: "testdata/classifier-license-dist", want: &analyzer.AnalysisResult{ Applications: []types.Application{ { Type: types.PythonPkg, - FilePath: "testdata/classifier-license.dist-info/METADATA", + FilePath: "setuptools-51.3.3.dist-info/METADATA", Libraries: types.Packages{ { Name: "setuptools", Version: "51.3.3", Licenses: []string{"MIT License"}, - FilePath: "testdata/classifier-license.dist-info/METADATA", + FilePath: "setuptools-51.3.3.dist-info/METADATA", }, }, }, @@ -103,19 +103,19 @@ func Test_packagingAnalyzer_Analyze(t *testing.T) { }, }, { - name: "wheel", - inputFile: "testdata/happy.dist-info/METADATA", + name: "wheel", + dir: "testdata/happy-dist", want: &analyzer.AnalysisResult{ Applications: []types.Application{ { Type: types.PythonPkg, - FilePath: "testdata/happy.dist-info/METADATA", + FilePath: "distlib-0.3.1.dist-info/METADATA", Libraries: types.Packages{ { Name: "distlib", Version: "0.3.1", Licenses: []string{"Python license"}, - FilePath: "testdata/happy.dist-info/METADATA", + FilePath: "distlib-0.3.1.dist-info/METADATA", }, }, }, @@ -123,27 +123,41 @@ func Test_packagingAnalyzer_Analyze(t *testing.T) { }, }, { - name: "egg zip doesn't contain required files", - inputFile: "testdata/no-required-files.egg", - want: nil, + name: "egg zip doesn't contain required files", + dir: "testdata/no-req-files", + want: &analyzer.AnalysisResult{}, + }, + { + name: "license file in dist.info", + dir: "testdata/license-file-dist", + want: &analyzer.AnalysisResult{ + Applications: []types.Application{ + { + Type: types.PythonPkg, + FilePath: "typing_extensions-4.4.0.dist-info/METADATA", + Libraries: []types.Package{ + { + Name: "typing_extensions", + Version: "4.4.0", + Licenses: []string{"BeOpen", "CNRI-Python-GPL-Compatible", "LicenseRef-MIT-Lucent", "Python-2.0"}, + FilePath: "typing_extensions-4.4.0.dist-info/METADATA", + }, + }, + }, + }, + }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - f, err := os.Open(tt.inputFile) - require.NoError(t, err) - defer f.Close() - stat, err := f.Stat() + a, err := newPackagingAnalyzer(analyzer.AnalyzerOptions{}) require.NoError(t, err) - - a := packagingAnalyzer{} - ctx := context.Background() - got, err := a.Analyze(ctx, analyzer.AnalysisInput{ - FilePath: tt.inputFile, - Info: stat, - Content: f, - Options: analyzer.AnalysisOptions{FileChecksum: tt.includeChecksum}, + got, err := a.PostAnalyze(context.Background(), analyzer.PostAnalysisInput{ + FS: os.DirFS(tt.dir), + Options: analyzer.AnalysisOptions{ + FileChecksum: tt.includeChecksum, + }, }) if tt.wantErr != "" { @@ -184,6 +198,11 @@ func Test_packagingAnalyzer_Required(t *testing.T) { filePath: "python3.8/site-packages/wrapt-1.12.1.dist-info/METADATA", want: true, }, + { + name: "wheel license", + filePath: "python3.8/site-packages/wrapt-1.12.1.dist-info/LICENSE", + want: true, + }, { name: "sad", filePath: "random/PKG-INFO", diff --git a/pkg/fanal/analyzer/language/python/packaging/testdata/classifier-license.dist-info/METADATA b/pkg/fanal/analyzer/language/python/packaging/testdata/classifier-license-dist/setuptools-51.3.3.dist-info/METADATA similarity index 100% rename from pkg/fanal/analyzer/language/python/packaging/testdata/classifier-license.dist-info/METADATA rename to pkg/fanal/analyzer/language/python/packaging/testdata/classifier-license-dist/setuptools-51.3.3.dist-info/METADATA diff --git a/pkg/fanal/analyzer/language/python/packaging/testdata/classifier-license.egg-info/PKG-INFO b/pkg/fanal/analyzer/language/python/packaging/testdata/classifier-license-egg/setuptools-51.3.3.egg-info/PKG-INFO similarity index 100% rename from pkg/fanal/analyzer/language/python/packaging/testdata/classifier-license.egg-info/PKG-INFO rename to pkg/fanal/analyzer/language/python/packaging/testdata/classifier-license-egg/setuptools-51.3.3.egg-info/PKG-INFO diff --git a/pkg/fanal/analyzer/language/python/packaging/testdata/kitchen-1.2.6-py2.7.egg b/pkg/fanal/analyzer/language/python/packaging/testdata/egg-zip/kitchen-1.2.6-py2.7.egg similarity index 100% rename from pkg/fanal/analyzer/language/python/packaging/testdata/kitchen-1.2.6-py2.7.egg rename to pkg/fanal/analyzer/language/python/packaging/testdata/egg-zip/kitchen-1.2.6-py2.7.egg diff --git a/pkg/fanal/analyzer/language/python/packaging/testdata/happy.dist-info/METADATA b/pkg/fanal/analyzer/language/python/packaging/testdata/happy-dist/distlib-0.3.1.dist-info/METADATA similarity index 100% rename from pkg/fanal/analyzer/language/python/packaging/testdata/happy.dist-info/METADATA rename to pkg/fanal/analyzer/language/python/packaging/testdata/happy-dist/distlib-0.3.1.dist-info/METADATA diff --git a/pkg/fanal/analyzer/language/python/packaging/testdata/happy.egg-info/PKG-INFO b/pkg/fanal/analyzer/language/python/packaging/testdata/happy-egg/distlib-0.3.1.egg-info/PKG-INFO similarity index 100% rename from pkg/fanal/analyzer/language/python/packaging/testdata/happy.egg-info/PKG-INFO rename to pkg/fanal/analyzer/language/python/packaging/testdata/happy-egg/distlib-0.3.1.egg-info/PKG-INFO diff --git a/pkg/fanal/analyzer/language/python/packaging/testdata/license-file-dist/typing_extensions-4.4.0.dist-info/LICENSE.txt b/pkg/fanal/analyzer/language/python/packaging/testdata/license-file-dist/typing_extensions-4.4.0.dist-info/LICENSE.txt new file mode 100644 index 000000000000..1df6b3b8de0e --- /dev/null +++ b/pkg/fanal/analyzer/language/python/packaging/testdata/license-file-dist/typing_extensions-4.4.0.dist-info/LICENSE.txt @@ -0,0 +1,254 @@ +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations, which became +Zope Corporation. In 2001, the Python Software Foundation (PSF, see +https://www.python.org/psf/) was formed, a non-profit organization +created specifically to own Python-related Intellectual Property. +Zope Corporation was a sponsoring member of the PSF. + +All Python releases are Open Source (see http://www.opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/pkg/fanal/analyzer/language/python/packaging/testdata/license-file-dist/typing_extensions-4.4.0.dist-info/METADATA b/pkg/fanal/analyzer/language/python/packaging/testdata/license-file-dist/typing_extensions-4.4.0.dist-info/METADATA new file mode 100644 index 000000000000..e4fc961591e5 --- /dev/null +++ b/pkg/fanal/analyzer/language/python/packaging/testdata/license-file-dist/typing_extensions-4.4.0.dist-info/METADATA @@ -0,0 +1,189 @@ +Metadata-Version: 2.1 +Name: typing_extensions +Version: 4.4.0 +Summary: Backported and Experimental Type Hints for Python 3.7+ +Keywords: annotations,backport,checker,checking,function,hinting,hints,type,typechecking,typehinting,typehints,typing +Author-email: "Guido van Rossum, Jukka Lehtosalo, Ɓukasz Langa, Michael Lee" +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: licenses/LICENSE.txt +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Topic :: Software Development +Project-URL: Bug Tracker, https://github.com/python/typing_extensions/issues +Project-URL: Changes, https://github.com/python/typing_extensions/blob/main/CHANGELOG.md +Project-URL: Documentation, https://typing.readthedocs.io/ +Project-URL: Home, https://github.com/python/typing_extensions +Project-URL: Q & A, https://github.com/python/typing/discussions +Project-URL: Repository, https://github.com/python/typing_extensions + +# Typing Extensions + +[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing) + +## Overview + +The `typing_extensions` module serves two related purposes: + +- Enable use of new type system features on older Python versions. For example, + `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows + users on previous Python versions to use it too. +- Enable experimentation with new type system PEPs before they are accepted and + added to the `typing` module. + +New features may be added to `typing_extensions` as soon as they are specified +in a PEP that has been added to the [python/peps](https://github.com/python/peps) +repository. If the PEP is accepted, the feature will then be added to `typing` +for the next CPython release. No typing PEP has been rejected so far, so we +haven't yet figured out how to deal with that possibility. + +Starting with version 4.0.0, `typing_extensions` uses +[Semantic Versioning](https://semver.org/). The +major version is incremented for all backwards-incompatible changes. +Therefore, it's safe to depend +on `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`, +where `x.y` is the first version that includes all features you need. + +`typing_extensions` supports Python versions 3.7 and higher. In the future, +support for older Python versions will be dropped some time after that version +reaches end of life. + +## Included items + +This module currently contains the following: + +- Experimental features + + - `override` (see PEP 698) + - The `default=` argument to `TypeVar`, `ParamSpec`, and `TypeVarTuple` (see PEP 696) + - The `infer_variance=` argument to `TypeVar` (see PEP 695) + +- In `typing` since Python 3.11 + + - `assert_never` + - `assert_type` + - `clear_overloads` + - `@dataclass_transform()` (see PEP 681) + - `get_overloads` + - `LiteralString` (see PEP 675) + - `Never` + - `NotRequired` (see PEP 655) + - `reveal_type` + - `Required` (see PEP 655) + - `Self` (see PEP 673) + - `TypeVarTuple` (see PEP 646; the `typing_extensions` version supports the `default=` argument from PEP 696) + - `Unpack` (see PEP 646) + +- In `typing` since Python 3.10 + + - `Concatenate` (see PEP 612) + - `ParamSpec` (see PEP 612; the `typing_extensions` version supports the `default=` argument from PEP 696) + - `ParamSpecArgs` (see PEP 612) + - `ParamSpecKwargs` (see PEP 612) + - `TypeAlias` (see PEP 613) + - `TypeGuard` (see PEP 647) + - `is_typeddict` + +- In `typing` since Python 3.9 + + - `Annotated` (see PEP 593) + +- In `typing` since Python 3.8 + + - `final` (see PEP 591) + - `Final` (see PEP 591) + - `Literal` (see PEP 586) + - `Protocol` (see PEP 544) + - `runtime_checkable` (see PEP 544) + - `TypedDict` (see PEP 589) + - `get_origin` (`typing_extensions` provides this function only in Python 3.7+) + - `get_args` (`typing_extensions` provides this function only in Python 3.7+) + +- In `typing` since Python 3.7 + + - `OrderedDict` + +- In `typing` since Python 3.5 or 3.6 (see [the typing documentation](https://docs.python.org/3.10/library/typing.html) for details) + + - `AsyncContextManager` + - `AsyncGenerator` + - `AsyncIterable` + - `AsyncIterator` + - `Awaitable` + - `ChainMap` + - `ClassVar` (see PEP 526) + - `ContextManager` + - `Coroutine` + - `Counter` + - `DefaultDict` + - `Deque` + - `NewType` + - `NoReturn` + - `overload` + - `Text` + - `Type` + - `TYPE_CHECKING` + - `get_type_hints` + +- The following have always been present in `typing`, but the `typing_extensions` versions provide + additional features: + + - `Any` (supports inheritance since Python 3.11) + - `NamedTuple` (supports multiple inheritance with `Generic` since Python 3.11) + - `TypeVar` (see PEPs 695 and 696) + +# Other Notes and Limitations + +Certain objects were changed after they were added to `typing`, and +`typing_extensions` provides a backport even on newer Python versions: + +- `TypedDict` does not store runtime information + about which (if any) keys are non-required in Python 3.8, and does not + honor the `total` keyword with old-style `TypedDict()` in Python + 3.9.0 and 3.9.1. `TypedDict` also does not support multiple inheritance + with `typing.Generic` on Python <3.11. +- `get_origin` and `get_args` lack support for `Annotated` in + Python 3.8 and lack support for `ParamSpecArgs` and `ParamSpecKwargs` + in 3.9. +- `@final` was changed in Python 3.11 to set the `.__final__` attribute. +- `@overload` was changed in Python 3.11 to make function overloads + introspectable at runtime. In order to access overloads with + `typing_extensions.get_overloads()`, you must use + `@typing_extensions.overload`. +- `NamedTuple` was changed in Python 3.11 to allow for multiple inheritance + with `typing.Generic`. +- Since Python 3.11, it has been possible to inherit from `Any` at + runtime. `typing_extensions.Any` also provides this capability. +- `TypeVar` gains two additional parameters, `default=` and `infer_variance=`, + in the draft PEPs 695 and 696, which are being considered for inclusion + in Python 3.12. + +There are a few types whose interface was modified between different +versions of typing. For example, `typing.Sequence` was modified to +subclass `typing.Reversible` as of Python 3.5.3. + +These changes are _not_ backported to prevent subtle compatibility +issues when mixing the differing implementations of modified classes. + +Certain types have incorrect runtime behavior due to limitations of older +versions of the typing module: + +- `ParamSpec` and `Concatenate` will not work with `get_args` and + `get_origin`. Certain PEP 612 special cases in user-defined + `Generic`s are also not available. + +These types are only guaranteed to work for static type checking. + +## Running tests + +To run tests, navigate into the appropriate source directory and run +`test_typing_extensions.py`. + diff --git a/pkg/fanal/analyzer/language/python/packaging/testdata/no-required-files.egg b/pkg/fanal/analyzer/language/python/packaging/testdata/no-req-files/no-required-files.egg similarity index 100% rename from pkg/fanal/analyzer/language/python/packaging/testdata/no-required-files.egg rename to pkg/fanal/analyzer/language/python/packaging/testdata/no-req-files/no-required-files.egg