ONNX
security
malware-detection
File size: 9,273 Bytes
d2507b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
package bundle

import (
	"path/filepath"
	"strings"
)

// allowlist.go holds the known-benign-pattern allowlist that controls the
// false-positive blast radius of bundle scanning. Many legitimate skills ship a
// native wheel, run curl|sh against their own release host, set soffice
// LD_PRELOAD for document conversion, or point pip/npm at a corporate mirror.
// These benign-but-scary idioms share surface features with attacks; the
// allowlist downgrades matching findings so the scanner is not an FP cannon that
// forces operators to globally suppress bundle_cross_file.
//
// SAFETY INVARIANT: the defanged exfil host (attacker.example, 198.51.100.0/24
// TEST-NET) is NEVER allowlisted, so a downgrade can never launder a real exfil
// signal.

// knownBenignHosts are package-registry/CDN hosts treated as legitimate mirror
// targets. A registry rewrite to one of these is a corporate-mirror idiom, not
// an attack. Matching is host-suffix aware (registry.corp.example matches an
// entry "corp.example") via isKnownBenignHost.
var knownBenignHosts = map[string]bool{
	// Default public registries (rewriting "to" the default is a no-op, but the
	// string still appears in legit configs).
	"registry.npmjs.org":     true,
	"pypi.org":               true,
	"files.pythonhosted.org": true,
	"rubygems.org":           true,
	"crates.io":              true,
	"static.crates.io":       true,
	"proxy.golang.org":       true,
	"sum.golang.org":         true,
	"repo.maven.apache.org":  true,
	"repo1.maven.org":        true,
	// Common trusted release/CDN hosts skills legitimately curl from.
	"github.com":                    true,
	"raw.githubusercontent.com":     true,
	"objects.githubusercontent.com": true,
	"codeload.github.com":           true,
	"registry.yarnpkg.com":          true,
	"cdn.jsdelivr.net":              true,
	"unpkg.com":                     true,
}

// benignHostSuffixes are corporate/enterprise mirror domains that legitimately
// host private registries. A rewrite to a sub-host of one of these is a
// known-benign corporate-mirror pattern.
var benignHostSuffixes = []string{
	"jfrog.io",
	"artifactory.com",
	"pkg.dev",   // Google Artifact Registry
	"azure.com", // Azure Artifacts
	"visualstudio.com",
	"nexus.example", // documented corporate-mirror placeholder in the corpus
	"corp.example",  // documented corporate-mirror placeholder in the corpus
}

// disallowedExfilHostFragments are NEVER allowlisted regardless of any other
// rule (defense against an allowlist entry accidentally covering the corpus's
// defanged exfil host).
var disallowedExfilHostFragments = []string{
	"attacker.example",
	"198.51.100.",
}

// isKnownBenignHost reports whether host (already lowercased, no scheme/path) is
// a recognized legitimate registry/CDN/corporate-mirror. The defanged exfil host
// is explicitly excluded.
func isKnownBenignHost(host string) bool {
	host = strings.ToLower(strings.TrimSpace(host))
	if host == "" {
		return false
	}
	// Strip a port if present.
	if i := strings.IndexByte(host, ':'); i >= 0 {
		host = host[:i]
	}
	for _, frag := range disallowedExfilHostFragments {
		if strings.Contains(host, frag) {
			return false
		}
	}
	if knownBenignHosts[host] {
		return true
	}
	for _, suf := range benignHostSuffixes {
		if host == suf || strings.HasSuffix(host, "."+suf) {
			return true
		}
	}
	return false
}

// internalHostSuffixes are multi-label corporate/private-network suffixes that
// indicate an internal mirror rather than a public exfil host. A registry
// rewrite pointing at one of these is the enterprise "configure the internal
// cache" idiom (e.g. pypi.internal.example.com, registry.internal.example.com)
// and must not corroborate on its own.
var internalHostSuffixes = []string{
	".internal",    // bare *.internal
	".intra",       // *.intra
	".corp",        // *.corp
	".lan",         // *.lan
	".home.arpa",   // RFC 8375 home-network reserved zone
	".localdomain", // common single-host local suffix
}

// internalHostInfixes are multi-label internal markers that appear as an inner
// label rather than a trailing suffix, e.g. "internal" in
// pypi.internal.example.com / registry.internal.example.com (the corpus's
// corporate-mirror placeholder). Matched as a dot-delimited label so a host like
// "internalattacker.example" does NOT match.
var internalHostInfixes = []string{
	".internal.",
	".intra.",
	".corp.",
}

// isInternalRegistryHost reports whether host (lowercased, no scheme/path) is an
// internal/private registry mirror destination: an RFC1918 / loopback IP, a
// localhost name, or a host under an internal/corp suffix or infix. A rewrite to
// such a host is a benign enterprise mirror idiom and must NOT corroborate.
//
// SAFETY: the defanged exfil host (attacker.example, 198.51.100.0/24 TEST-NET)
// is checked FIRST and can never be classified as internal, so a real exfil
// signal can never be laundered through this downgrade.
func isInternalRegistryHost(host string) bool {
	host = strings.ToLower(strings.TrimSpace(host))
	if host == "" {
		return false
	}
	// Unwrap a bracketed IPv6 literal, optionally followed by ":port".
	if strings.HasPrefix(host, "[") {
		if end := strings.IndexByte(host, ']'); end >= 0 {
			host = host[1:end]
		} else {
			host = strings.TrimLeft(host, "[")
		}
	} else if strings.Count(host, ":") == 1 {
		// A single colon is a host:port separator (bare IPv6 has >=2 colons).
		host = host[:strings.IndexByte(host, ':')]
	}
	if host == "" {
		return false
	}
	// SAFETY GUARD FIRST: never downgrade the defanged exfil host.
	for _, frag := range disallowedExfilHostFragments {
		if strings.Contains(host, frag) {
			return false
		}
	}
	// Loopback / localhost names and literals.
	if host == "localhost" || host == "127.0.0.1" || host == "::1" ||
		strings.HasSuffix(host, ".localhost") {
		return true
	}
	// RFC1918 private IPv4 ranges.
	if isPrivateIPv4(host) {
		return true
	}
	// Internal/corp multi-label suffixes (trailing).
	for _, suf := range internalHostSuffixes {
		if strings.HasSuffix(host, suf) {
			return true
		}
	}
	// Internal/corp markers as an inner label (e.g. *.internal.example.com).
	for _, inf := range internalHostInfixes {
		if strings.Contains(host, inf) {
			return true
		}
	}
	return false
}

// isPrivateIPv4 reports whether host is a dotted-quad in an RFC1918 private
// range: 10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16. Non-IP hosts return
// false.
func isPrivateIPv4(host string) bool {
	parts := strings.Split(host, ".")
	if len(parts) != 4 {
		return false
	}
	octets := make([]int, 4)
	for i, p := range parts {
		if p == "" || len(p) > 3 {
			return false
		}
		n := 0
		for _, c := range p {
			if c < '0' || c > '9' {
				return false
			}
			n = n*10 + int(c-'0')
		}
		if n > 255 {
			return false
		}
		octets[i] = n
	}
	switch {
	case octets[0] == 10:
		return true
	case octets[0] == 172 && octets[1] >= 16 && octets[1] <= 31:
		return true
	case octets[0] == 192 && octets[1] == 168:
		return true
	}
	return false
}

// isKnownBenignNativePattern reports whether a native binary file matches a
// common legitimate shipping pattern (a signed/platform-tagged native wheel or
// a Node native addon) so a bare .so does not self-escalate. This is a
// precision lever only: a binary that embeds the exfil host or dangerous
// symbols is handled by the analyzer BEFORE this downgrade is consulted.
func isKnownBenignNativePattern(f *File) bool {
	if f == nil {
		return false
	}
	base := strings.ToLower(filepath.Base(f.RelPath))
	// Node native addon convention.
	if strings.HasSuffix(base, ".node") {
		return true
	}
	// Python extension modules carry an ABI tag, e.g.
	// "_speedups.cpython-312-x86_64-linux-gnu.so" or "...-darwin.so".
	if strings.HasSuffix(base, ".so") || strings.HasSuffix(base, ".dylib") {
		if strings.Contains(base, ".cpython-") ||
			strings.Contains(base, ".abi3.") ||
			strings.Contains(base, "-x86_64-") ||
			strings.Contains(base, "-aarch64-") ||
			strings.Contains(base, "-arm64-") ||
			strings.Contains(base, "-darwin") ||
			strings.Contains(base, "-linux-gnu") {
			return true
		}
	}
	return false
}

// isKnownBenignScriptIdiom reports whether a script line matches a benign-but-
// scary idiom that should not, on its own, escalate. Examples: setting
// LD_PRELOAD for soffice/libreoffice document conversion, or rustup/nvm-style
// installers fetching from their own canonical host. Lines containing the
// defanged exfil host are never benign.
func isKnownBenignScriptIdiom(line string) bool {
	low := strings.ToLower(line)
	for _, frag := range disallowedExfilHostFragments {
		if strings.Contains(low, frag) {
			return false
		}
	}
	// soffice/libreoffice LD_PRELOAD doc-conversion idiom.
	if strings.Contains(low, "ld_preload") &&
		(strings.Contains(low, "soffice") || strings.Contains(low, "libreoffice") ||
			strings.Contains(low, "unoconv")) {
		return true
	}
	// Canonical first-party installers fetched from their own hosts.
	benignInstallerHosts := []string{
		"sh.rustup.rs", "static.rust-lang.org",
		"raw.githubusercontent.com/nvm-sh",
		"get.docker.com", "deb.nodesource.com", "rpm.nodesource.com",
		"install.python-poetry.org",
	}
	if matchedAny(low, benignInstallerHosts) {
		return true
	}
	return false
}