Skip to content

Commit 6e902c8

Browse files
committed
feat: SCA-9 add dart pubspec supports
1 parent 3552dea commit 6e902c8

File tree

4 files changed

+283
-3
lines changed

4 files changed

+283
-3
lines changed

module/module.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package module
22

33
import (
4+
"os"
5+
"strconv"
6+
"strings"
7+
48
"github.com/murphysecurity/murphysec/model"
59
"github.com/murphysecurity/murphysec/module/arkts"
610
"github.com/murphysecurity/murphysec/module/bundler"
@@ -18,6 +22,7 @@ import (
1822
"github.com/murphysecurity/murphysec/module/perl"
1923
"github.com/murphysecurity/murphysec/module/pnpm"
2024
"github.com/murphysecurity/murphysec/module/poetry"
25+
"github.com/murphysecurity/murphysec/module/pubspec"
2126
"github.com/murphysecurity/murphysec/module/python"
2227
"github.com/murphysecurity/murphysec/module/rebar3"
2328
"github.com/murphysecurity/murphysec/module/renv"
@@ -26,9 +31,6 @@ import (
2631
"github.com/murphysecurity/murphysec/utils"
2732
"github.com/repeale/fp-go"
2833
"github.com/samber/lo"
29-
"os"
30-
"strconv"
31-
"strings"
3234
)
3335

3436
var Inspectors []model.Inspector
@@ -64,6 +66,7 @@ func init() {
6466
Inspectors = append(Inspectors, sbt.Inspector{})
6567
Inspectors = append(Inspectors, yarn.Inspector{})
6668
Inspectors = append(Inspectors, luarocks.Inspector{})
69+
Inspectors = append(Inspectors, pubspec.Inspector{})
6770

6871
var enabled = fp.Pipe6(os.Getenv, utils.SplitBy(","), fp.Map(strings.TrimSpace), fp.Filter(lo.IsNotEmpty[string]), fp.Map(strings.ToLower), utils.ToSet[string])("MPS_ENABLED_INSPECTORS")
6972
if len(enabled) > 0 {

module/pubspec/pubspec.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package pubspec
2+
3+
import (
4+
"bufio"
5+
"context"
6+
"io"
7+
"os"
8+
"path/filepath"
9+
"strings"
10+
11+
"github.com/murphysecurity/murphysec/infra/logctx"
12+
"github.com/murphysecurity/murphysec/model"
13+
"github.com/murphysecurity/murphysec/utils"
14+
"github.com/repeale/fp-go"
15+
"github.com/samber/lo"
16+
"gopkg.in/yaml.v3"
17+
)
18+
19+
type Inspector struct{}
20+
21+
func (i Inspector) String() string {
22+
return "Pubspec"
23+
}
24+
25+
func (i Inspector) CheckDir(ctx context.Context, dir string) bool {
26+
return utils.IsFile(filepath.Join(dir, lockfileName))
27+
}
28+
29+
func (i Inspector) InspectProject(ctx context.Context) (e error) {
30+
var logger = logctx.Use(ctx)
31+
var inspTask = model.UseInspectionTask(ctx)
32+
lockPath := filepath.Join(inspTask.Dir(), lockfileName)
33+
var f *os.File
34+
f, e = os.Open(lockPath)
35+
if e != nil {
36+
return
37+
}
38+
defer func() { utils.CloseLogErrZap(f, logger) }()
39+
var deps []model.DependencyItem
40+
deps, e = parseFile(ctx, f)
41+
if e != nil {
42+
return
43+
}
44+
var module = model.Module{
45+
ModulePath: lockPath,
46+
PackageManager: "pubspec",
47+
Dependencies: deps,
48+
ScanStrategy: model.ScanStrategyNormal,
49+
}
50+
inspTask.AddModule(module)
51+
return
52+
}
53+
54+
func parseFile(ctx context.Context, reader io.Reader) (r []model.DependencyItem, e error) {
55+
var parsedLockfile *pubspecLockfile
56+
e = yaml.NewDecoder(bufio.NewReader(reader)).Decode(&parsedLockfile)
57+
if e != nil {
58+
return
59+
}
60+
r = fp.Map(func(it lo.Entry[string, pubspecLockPackage]) model.DependencyItem {
61+
dep := model.DependencyItem{
62+
Component: model.Component{
63+
CompName: it.Key,
64+
CompVersion: it.Value.Version,
65+
EcoRepo: model.EcoRepo{
66+
Ecosystem: "pubspec",
67+
},
68+
},
69+
IsOnline: model.IsOnlineTrue(),
70+
}
71+
for _, flag := range strings.Split(it.Value.Dependency, " ") {
72+
switch flag {
73+
case "dev":
74+
dep.IsOnline.SetOnline(false)
75+
case "direct":
76+
dep.IsDirectDependency = true
77+
}
78+
}
79+
return dep
80+
})(lo.Entries(parsedLockfile.Packages))
81+
return
82+
}
83+
84+
func (i Inspector) SupportFeature(feature model.InspectorFeature) bool {
85+
return false
86+
}
87+
88+
var _ model.Inspector = &Inspector{}
89+
90+
const lockfileName = "pubspec.lock"
91+
92+
type pubspecLockPackage struct {
93+
Version string `yaml:"version"`
94+
Dependency string `yaml:"dependency"`
95+
}
96+
type pubspecLockfile struct {
97+
Packages map[string]pubspecLockPackage `yaml:"packages,omitempty"`
98+
}

module/pubspec/pubspec_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package pubspec
2+
3+
import (
4+
"bytes"
5+
"context"
6+
_ "embed"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
//go:embed testdata-1
13+
var testdata1 []byte
14+
15+
func TestName(t *testing.T) {
16+
var d, e = parseFile(context.TODO(), bytes.NewReader(testdata1))
17+
assert.NoError(t, e)
18+
assert.Equal(t, 23, len(d))
19+
}

module/pubspec/testdata-1

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# Generated by pub
2+
# See https://dart.dev/tools/pub/glossary#lockfile
3+
packages:
4+
async:
5+
dependency: transitive
6+
description:
7+
name: async
8+
url: "https://pub.dartlang.org"
9+
source: hosted
10+
version: "2.6.1"
11+
avatar_glow:
12+
dependency: "direct main"
13+
description:
14+
name: avatar_glow
15+
url: "https://pub.dartlang.org"
16+
source: hosted
17+
version: "1.0.0"
18+
boolean_selector:
19+
dependency: transitive
20+
description:
21+
name: boolean_selector
22+
url: "https://pub.dartlang.org"
23+
source: hosted
24+
version: "2.1.0"
25+
characters:
26+
dependency: transitive
27+
description:
28+
name: characters
29+
url: "https://pub.dartlang.org"
30+
source: hosted
31+
version: "1.1.0"
32+
charcode:
33+
dependency: transitive
34+
description:
35+
name: charcode
36+
url: "https://pub.dartlang.org"
37+
source: hosted
38+
version: "1.2.0"
39+
clock:
40+
dependency: transitive
41+
description:
42+
name: clock
43+
url: "https://pub.dartlang.org"
44+
source: hosted
45+
version: "1.1.0"
46+
collection:
47+
dependency: transitive
48+
description:
49+
name: collection
50+
url: "https://pub.dartlang.org"
51+
source: hosted
52+
version: "1.15.0"
53+
cupertino_icons:
54+
dependency: "direct main"
55+
description:
56+
name: cupertino_icons
57+
url: "https://pub.dartlang.org"
58+
source: hosted
59+
version: "1.0.3"
60+
fake_async:
61+
dependency: transitive
62+
description:
63+
name: fake_async
64+
url: "https://pub.dartlang.org"
65+
source: hosted
66+
version: "1.2.0"
67+
flutter:
68+
dependency: "direct main"
69+
description: flutter
70+
source: sdk
71+
version: "0.0.0"
72+
flutter_test:
73+
dependency: "direct dev"
74+
description: flutter
75+
source: sdk
76+
version: "0.0.0"
77+
matcher:
78+
dependency: transitive
79+
description:
80+
name: matcher
81+
url: "https://pub.dartlang.org"
82+
source: hosted
83+
version: "0.12.10"
84+
meta:
85+
dependency: transitive
86+
description:
87+
name: meta
88+
url: "https://pub.dartlang.org"
89+
source: hosted
90+
version: "1.3.0"
91+
path:
92+
dependency: transitive
93+
description:
94+
name: path
95+
url: "https://pub.dartlang.org"
96+
source: hosted
97+
version: "1.8.0"
98+
sky_engine:
99+
dependency: transitive
100+
description: flutter
101+
source: sdk
102+
version: "0.0.99"
103+
source_span:
104+
dependency: transitive
105+
description:
106+
name: source_span
107+
url: "https://pub.dartlang.org"
108+
source: hosted
109+
version: "1.8.1"
110+
stack_trace:
111+
dependency: transitive
112+
description:
113+
name: stack_trace
114+
url: "https://pub.dartlang.org"
115+
source: hosted
116+
version: "1.10.0"
117+
stream_channel:
118+
dependency: transitive
119+
description:
120+
name: stream_channel
121+
url: "https://pub.dartlang.org"
122+
source: hosted
123+
version: "2.1.0"
124+
string_scanner:
125+
dependency: transitive
126+
description:
127+
name: string_scanner
128+
url: "https://pub.dartlang.org"
129+
source: hosted
130+
version: "1.1.0"
131+
term_glyph:
132+
dependency: transitive
133+
description:
134+
name: term_glyph
135+
url: "https://pub.dartlang.org"
136+
source: hosted
137+
version: "1.2.0"
138+
test_api:
139+
dependency: transitive
140+
description:
141+
name: test_api
142+
url: "https://pub.dartlang.org"
143+
source: hosted
144+
version: "0.3.0"
145+
typed_data:
146+
dependency: transitive
147+
description:
148+
name: typed_data
149+
url: "https://pub.dartlang.org"
150+
source: hosted
151+
version: "1.3.0"
152+
vector_math:
153+
dependency: transitive
154+
description:
155+
name: vector_math
156+
url: "https://pub.dartlang.org"
157+
source: hosted
158+
version: "2.1.0"
159+
sdks:
160+
dart: ">=2.12.0 <3.0.0"

0 commit comments

Comments
 (0)