github.com/google/osv-scalibr@v0.4.1/guidedremediation/internal/parser/parser.go (about) 1 // Copyright 2025 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 parser provides functions for parsing and writing manifest and lockfile files. 16 package parser 17 18 import ( 19 "fmt" 20 "path/filepath" 21 22 "deps.dev/util/resolve" 23 scalibrfs "github.com/google/osv-scalibr/fs" 24 "github.com/google/osv-scalibr/guidedremediation/internal/lockfile" 25 "github.com/google/osv-scalibr/guidedremediation/internal/manifest" 26 "github.com/google/osv-scalibr/guidedremediation/result" 27 ) 28 29 // ParseManifest parses a manifest file into a manifest.Manifest. 30 func ParseManifest(path string, rw manifest.ReadWriter) (manifest.Manifest, error) { 31 fsys, path, err := fsAndPath(path) 32 if err != nil { 33 return nil, err 34 } 35 36 m, err := rw.Read(path, fsys) 37 if err != nil { 38 return nil, fmt.Errorf("error reading manifest: %w", err) 39 } 40 return m, nil 41 } 42 43 // ParseLockfile parses a lockfile file into a resolve.Graph. 44 func ParseLockfile(path string, rw lockfile.ReadWriter) (*resolve.Graph, error) { 45 fsys, path, err := fsAndPath(path) 46 if err != nil { 47 return nil, err 48 } 49 50 g, err := rw.Read(path, fsys) 51 if err != nil { 52 return nil, fmt.Errorf("error reading lockfile: %w", err) 53 } 54 return g, nil 55 } 56 57 // WriteManifestPatches writes the patches to the manifest file. 58 func WriteManifestPatches(path string, m manifest.Manifest, patches []result.Patch, rw manifest.ReadWriter) error { 59 fsys, _, err := fsAndPath(path) 60 if err != nil { 61 return err 62 } 63 64 return rw.Write(m, fsys, patches, path) 65 } 66 67 // WriteLockfilePatches writes the patches to the lockfile file. 68 func WriteLockfilePatches(path string, patches []result.Patch, rw lockfile.ReadWriter) error { 69 fsys, relPath, err := fsAndPath(path) 70 if err != nil { 71 return err 72 } 73 74 return rw.Write(relPath, fsys, patches, path) 75 } 76 77 func fsAndPath(path string) (scalibrfs.FS, string, error) { 78 // We need a DirFS that can potentially access files in parent directories from the file. 79 // But you cannot escape the base directory of dirfs. 80 // e.g. "pkg/core/pom.xml" may have a parent at "pkg/parent/pom.xml", 81 // if we had fsys := scalibrfs.DirFS("pkg/core"), we can't do fsys.Open("../parent/pom.xml") 82 // 83 // Since we don't know ahead of time which files might be needed, 84 // we must use the system root as the directory. 85 86 absPath, err := filepath.Abs(path) 87 if err != nil { 88 return nil, "", err 89 } 90 91 // Get the path relative to the root (i.e. without the leading '/') 92 // On Windows, we need the path relative to the drive letter, 93 // which also means we can't open files across drives. 94 root := filepath.VolumeName(absPath) + "/" 95 relPath, err := filepath.Rel(root, absPath) 96 if err != nil { 97 return nil, "", err 98 } 99 relPath = filepath.ToSlash(relPath) 100 101 return scalibrfs.DirFS(root), relPath, nil 102 }