generated from ossf/project-template
-
Notifications
You must be signed in to change notification settings - Fork 16
/
reportio.go
134 lines (123 loc) · 3.66 KB
/
reportio.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright 2023 Malicious Packages Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package reportio
import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"github.com/ossf/malicious-packages/internal/report"
)
const (
filenameReadme = "README.md"
)
var (
ErrPathNotLocal = errors.New("report path is not local")
ErrInvalidPathStructure = errors.New("invalid report path structure")
)
// ValidatePath ensures path is a valid place to put OSV reports.
//
// This method enforces that every path is local, and that it has at least two
// path elements - i.e. an ecosystem, and a package name.
func ValidatePath(path string) error {
if !filepath.IsLocal(path) {
return ErrPathNotLocal
}
path = filepath.Dir(filepath.Dir(filepath.Join("local", filepath.Clean(path))))
if path == "" || path == "." {
return ErrInvalidPathStructure
}
return nil
}
// OriginExistsInPaths returns true if an OSV report exists with an origin that
// exists with the same sourceID and shasum.
//
// The function iterates across each of the base paths in bases and joins them
// with path using `filepath.Join(base, path)`.
//
// An error is returned if there is an error reading the OSV reports, or the
// filesystem.
func OriginExistsInPaths(path string, bases []string, sourceID, shasum string) (bool, error) {
reports, err := ReportsInPaths(path, bases)
if err != nil {
return false, err
}
for _, n := range reports {
r, err := report.FromFile(n)
if err != nil {
return false, fmt.Errorf("failed loading %s: %w", n, err)
}
if r.HasOrigin(sourceID, shasum) {
return true, nil
}
}
// No reports, so no origins can exist.
return false, nil
}
// ReportsInPaths returns a slice with all the filepaths to all OSV reports
// that share the same path (i.e. ecosystem and package name) across the
// directories in bases.
//
// The function iterates across each of the base paths in bases and joins them
// with path using `filepath.Join(base, path)`.
//
// An error will be returned if there is an error reading the the filesystem.
func ReportsInPaths(path string, bases []string) ([]string, error) {
var reports []string
for _, base := range bases {
fp := filepath.Clean(filepath.Join(base, path))
rs, err := reportsInPath(fp)
if err != nil {
return nil, err
}
reports = append(reports, rs...)
}
return reports, nil
}
func reportsInPath(path string) ([]string, error) {
var reports []string
entries, err := os.ReadDir(path)
if os.IsNotExist(err) {
// path doesn't exist, so there are no reports here.
return reports, nil
}
if err != nil {
return nil, fmt.Errorf("failed to read dir %s: %w", path, err)
}
for _, entry := range entries {
if !IsPossibleReport(entry.Name(), entry.Type()) {
continue
}
n := filepath.Join(path, entry.Name())
reports = append(reports, n)
}
return reports, nil
}
// IsPossibleReport returns true if the given file is possibly a report.
func IsPossibleReport(name string, mode fs.FileMode) bool {
// Directories are not reports.
if mode.IsDir() {
return false
}
// Dot files are not reports.
if name[0] == '.' {
return false
}
// Readme files are not reports.
if name == filenameReadme {
return false
}
return true
}