已开启
go代码安全扫描问题 #320
yangna1310创建于  19 天前
yangna1310
yangna1310成员
19 天前 创建

【缺陷描述】:请补充详细的缺陷问题现象描述

go代码安全扫描问题

一、缺陷信息

发现 7 个问题(🔴 致命 0 · 🟡 重要 0 · 🟢 建议 5 · ⚠️ 疑似 2)

该 BUILD 树为纯净上游 Go 1.24.2 标准库,经 Go 团队与社区长期审计,未发现可被远程/未认证利用的致命或重要漏洞。本次审计所立问题均为:遗留弱加密算法(向后兼容所需)、用户可选关闭证书校验的特性、网络响应体无界读取的潜在 DoS、以及一处用户配置异常导致的 panic(健壮性)。所有发现均附带真实 file:line、调用栈与修复建议。Go 工具链在命令执行方面采用 argv 数组(无 shell)、配合 git --end-of-options 与 module.CheckPath 校验,命令注入面已被有效收敛。


🟢 [建议] F1 — 遗留 PEM 加密使用 MD5 KDF + DES/3DES 弱加密算法

CWE: CWE-327(使用已被破解或有缺陷的加密算法)、CWE-916(口令派生函数迭代次数不足)
规则: R03-weak-crypto / R03-weak-kdf
描述: crypto/x509/pem_decrypt.go 实现 RFC 1423 遗留 PEM 加密。rfc1423Algos 将 PEMCipherDES(DES-CBC,56 位密钥)与 PEMCipher3DES 列为可选算法;deriveKey 使用 md5.New() 单轮迭代从口令派生密钥(无 PBKDF2/盐迭代)。DES 56 位密钥可被暴力破解,MD5 已被攻破(碰撞攻击),单轮无迭代 KDF 对字典攻击几乎无防护。该文件本身在 line 100-103 的 IsEncryptedPEMBlock 文档注释中已明确标注「Legacy PEM encryption as specified in RFC 1423 is insecure by design」。
影响: 当用户显式选择 PEMCipherDES/PEMCipher3DES 加密 PEM 私钥时,加密强度远低于 AES;MD5 单轮 KDF 使口令字典攻击成本极低。攻击者获得 PEM 文件后可离线恢复私钥。
CVSS: 3.5(Low)— 需攻击者已持有加密 PEM 文件,且默认/推荐算法为 AES。
数据来源: 遗留 PEM 文件(OpenSSL 历史格式)
风险传播路径: 用户调用 x509.EncryptPEMBlock(rand, blockType, data, password, PEMCipherDES) → 选中 rfc1423Algos[PEMCipherDES] → deriveKey(password, salt) 用 MD5 派生 8 字节密钥 → des.NewCipher 加密。

调用栈

x509.EncryptPEMBlock()           [crypto/x509/pem_decrypt.go:EncryptPEMBlock]
  -> rfc1423Algo.cipherFunc()    [crypto/x509/pem_decrypt.go:49  des.NewCipher]
     -> rfc1423Algo.deriveKey()  [crypto/x509/pem_decrypt.go:82  md5.New 单轮 KDF]

问题代码

// 文件: crypto/x509/pem_decrypt.go:46-57  DES/3DES 列为可选算法
var rfc1423Algos = []rfc1423Algo{{
    cipher:     PEMCipherDES,
    name:       "DES-CBC",
    cipherFunc: des.NewCipher,
    keySize:    8,
    blockSize:  des.BlockSize,
}, {
    cipher:     PEMCipher3DES,
    name:       "DES-EDE3-CBC",
    cipherFunc: des.NewTripleDESCipher,
    keySize:    24,
    blockSize:  des.BlockSize,
}, /* ... AES128/192/256 ... */}

// 文件: crypto/x509/pem_decrypt.go:82-96  MD5 单轮口令派生
func (c rfc1423Algo) deriveKey(password, salt []byte) []byte {
    hash := md5.New()           // 弱哈希
    out := make([]byte, c.keySize)
    var digest []byte
    for i := 0; i < len(out); i += len(digest) {
        hash.Reset()
        hash.Write(digest)
        hash.Write(password)
        hash.Write(salt)
        digest = hash.Sum(digest[:0])  // 单轮,无迭代计数
        copy(out[i:], digest)
    }
    return out
}

调用链分析

层次 函数 文件:行号 角色
1 x509.EncryptPEMBlock / DecryptPEMBlock crypto/x509/pem_decrypt.go 入口,用户选择算法
2 rfc1423Algo.deriveKey crypto/x509/pem_decrypt.go:82 口令派生,MD5 单轮
3 des.NewCipher / des.NewTripleDESCipher crypto/x509/pem_decrypt.go:49/55 弱分组密码

修复建议

// 1) 不建议修改上游 API;应用层应避免使用 PEMCipherDES/PEMCipher3DES,
//    新加密一律使用 PEMCipherAES256。
// 2) 若需保护遗留已加密 PEM,应迁移至 PKCS#8 (crypto/x509.MarshalPKCS8PrivateKey)
//    配合 AES-256-CBC + PBKDF2/scrypt 高迭代 KDF。
// 3) 文档/编译期告警:可在应用层 lint 规则中标记 PEMCipherDES/PEMCipher3DES 为禁用。
encBlock, err := x509.EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", pemData, password, x509.PEMCipherAES256)

🟢 [建议] F2 — TLS 1.0/1.1 PRF 与握手使用 MD5(协议遗留)

CWE: CWE-327(使用已被破解或有缺陷的加密算法)
规则: R03-weak-crypto
描述: crypto/tls/prf.go 的 prf10 实现 TLS 1.0 PRF,按 RFC 2246 使用 MD5+SHA1 双哈希;finishedHash 在 TLS 1.0/1.1 下额外维护 clientMD5/serverMD5 并在 Finished 消息中拼接 MD5 摘要;crypto/tls/key_agreement.go 的 md5SHA1Hash 实现 TLS 1.0 混合哈希。MD5 已被攻破,但 TLS 1.0/1.1 协议规范强制使用 MD5。
影响: 仅当协商使用 TLS 1.0/1.1 时受影响。Go 1.24 默认最小版本为 TLS 1.2(crypto/tls/common.go:758),服务端 TLS 1.0 仅在 GODEBUG=tls10server=1 时启用。默认配置不受影响。
CVSS: 2.0(Low)— 仅影响显式启用 TLS 1.0/1.1 的遗留连接。
数据来源: TLS 协议版本协商
风险传播路径: tls.Client 握手 → prfAndHashForVersion(VersionTLS10/11) 返回 prf10 → pHash(..., hashMD5) → MD5 参与密钥派生与 Finished 校验。

调用栈

tls.Conn.handshake()                       [crypto/tls/handshake_client.go / handshake_server.go]
  -> prfForVersion(VersionTLS10|VersionTLS11)  [crypto/tls/prf.go:104]
     -> prf10()                             [crypto/tls/prf.go:51]
        -> pHash(result, s1, labelAndSeed, hashMD5)  [crypto/tls/prf.go:61  md5.New]
  -> finishedHash.Write() / Sum()           [crypto/tls/prf.go:166,191,208  MD5 拼入 Finished]
  -> md5SHA1Hash() (RSA key exchange)       [crypto/tls/key_agreement.go:117  md5.New]

问题代码

// 文件: crypto/tls/prf.go:50-70  TLS 1.0 PRF 使用 MD5
func prf10(secret []byte, label string, seed []byte, keyLen int) []byte {
    result := make([]byte, keyLen)
    hashSHA1 := sha1.New
    hashMD5 := md5.New
    // ...
    s1, s2 := splitPreMasterSecret(secret)
    pHash(result, s1, labelAndSeed, hashMD5)   // MD5 参与密钥派生
    // ...
}

// 文件: crypto/tls/prf.go:90-93  TLS 1.0/1.1 路由到 prf10
func prfAndHashForVersion(version uint16, suite *cipherSuite) (prfFunc, crypto.Hash) {
    switch version {
    case VersionTLS10, VersionTLS11:
        return prf10, crypto.Hash(0)
    // ...
}

// 文件: crypto/tls/key_agreement.go:115-125  TLS 1.0 混合哈希
func md5SHA1Hash(slices [][]byte) []byte {
    md5sha1 := make([]byte, md5.Size+sha1.Size)
    hmd5 := md5.New()
    for _, slice := range slices {
        hmd5.Write(slice)
    }
    copy(md5sha1, hmd5.Sum(nil))
    copy(md5sha1[md5.Size:], sha1Hash(slices))
    return md5sha1
}

调用链分析

层次 函数 文件:行号 角色
1 tls.Conn.handshake crypto/tls/handshake_*.go 入口,协商版本
2 prfForVersion / prfAndHashForVersion crypto/tls/prf.go:90,104 按 TLS 版本选择 PRF
3 prf10 crypto/tls/prf.go:51 TLS 1.0 PRF,使用 MD5
4 pHash / md5SHA1Hash crypto/tls/prf.go:61 / key_agreement.go:117 MD5 实际写入处

修复建议

// 应用层应在 tls.Config 中强制最小版本为 TLS 1.2,彻底规避 MD5 路径:
config := &tls.Config{
    MinVersion: tls.VersionTLS12, // 拒绝 TLS 1.0/1.1
}
// 服务端不要设置 GODEBUG=tls10server=1(默认即禁用 TLS 1.0)。
// 上游 stdlib 此处为协议强制,无法移除 MD5;正确缓解在配置层。

🟢 [建议] F3 — pprof 工具在 https+insecure 协议下跳过 TLS 证书校验

CWE: CWE-295(证书校验不当)
规则: R03-cert-validation
描述: cmd/pprof/pprof.go 的 getProfile 在 URL scheme 为 https+insecure 时构造 tls.Config{InsecureSkipVerify: true},跳过服务器证书链与主机名校验,随后用该配置发起 HTTPS GET 拉取 profile 数据。
影响: 当用户以 https+insecure://host/... 形式拉取 profile 时,连接可被中间人(MITM)劫持/窃听/篡改 profile 数据。该 scheme 为用户显式输入的特殊协议,属 opt-in 行为;但一旦误用(如把生产 profile 端点写成 https+insecure),即丧失传输安全。
CVSS: 2.7(Low)— 需用户显式使用 https+insecure scheme,且攻击者处于网络路径中间人位置。
数据来源: 用户在 pprof 命令行传入的 URL
风险传播路径: pprof fetcher → getProfile("https+insecure://...") → tls.Config{InsecureSkipVerify:true} → http.Client.Get → MITM 可篡改 profile 响应。

调用栈

pprof.fetcher()                  [cmd/pprof/pprof.go:fetcher]
  -> getProfile(sourceURL)       [cmd/pprof/pprof.go:78]
     -> url.Parse(source)        [cmd/pprof/pprof.go:79]
     -> tls.Config{InsecureSkipVerify:true}  [cmd/pprof/pprof.go:86  scheme=="https+insecure"]
        -> http.Client.Get(source)           [cmd/pprof/pprof.go:100]

问题代码

// 文件: cmd/pprof/pprof.go:84-91
var tlsConfig *tls.Config
if url.Scheme == "https+insecure" {
    tlsConfig = &tls.Config{
        InsecureSkipVerify: true,   // 跳过证书校验
    }
    url.Scheme = "https"
    source = url.String()
}

调用链分析

层次 函数 文件:行号 角色
1 pprof.fetcher cmd/pprof/pprof.go:fetcher 入口,接收 URL
2 getProfile cmd/pprof/pprof.go:78 解析 scheme
3 tls.Config 构造 cmd/pprof/pprof.go:86 InsecureSkipVerify=true
4 http.Client.Get cmd/pprof/pprof.go:100 发起无校验 TLS 请求

修复建议

// 1) 文档/告警:在启用 https+insecure 时向 stderr 打印明显安全警告,
//    提示该连接不受 MITM 保护,仅限受控调试环境。
// 2) 优先使用显式 CA 指定而非整体跳过校验:支持 -cacert 指定自签 CA。
// 3) 用户侧:不要对非本机/不可信网络使用 https+insecure,仅限本地 loopback 调试。

🟢 [建议] F4 — go 命令在 GOINSECURE 模式下跳过 HTTPS 模块拉取的证书校验

CWE: CWE-295(证书校验不当)
规则: R03-cert-validation
描述: cmd/go/internal/web/http.go 定义 impatientInsecureHTTPClient,其 TLSClientConfig.InsecureSkipVerify = true。该 client 在 get() 中当 security == Insecure && url.Scheme == "https" 时被选用(line 134-135),即用户设置 GOINSECURE 环境变量后,go get 对 HTTPS 模块源/代理的请求跳过证书校验。
影响: 设置 GOINSECURE 后,模块拉取可被 MITM 篡改/窃听,攻击者可注入恶意模块代码或篡改 go.sum 校验和前的元数据。该行为是 GOINSECURE 的设计语义(用于自签证书的内网代理),但用户若将 GOINSECURE 设得过宽(如 GOINSECURE=*),将整体丧失模块拉取的传输安全。
CVSS: 2.7(Low)— 需用户显式设置 GOINSECURE 且攻击者处于网络路径。
数据来源: GOINSECURE 环境变量(用户配置)
风险传播路径: go get → web.get(Insecure) → fetch(url) → security==Insecure && https → impatientInsecureHTTPClient.Do(req) → MITM。

调用栈

go get / web.Get(Insecure)                       [cmd/go/internal/web/api.go:Get]
  -> web.get(security=Insecure, url)             [cmd/go/internal/web/http.go:76]
     -> fetch(url)                               [cmd/go/internal/web/http.go:119]
        -> security==Insecure && url.Scheme=="https"  [cmd/go/internal/web/http.go:134]
           -> impatientInsecureHTTPClient.Do(req)     [cmd/go/internal/web/http.go:159]
              -> TLSClientConfig.InsecureSkipVerify=true  [cmd/go/internal/web/http.go:43]

问题代码

// 文件: cmd/go/internal/web/http.go:37-46
var impatientInsecureHTTPClient = &http.Client{
    CheckRedirect: checkRedirect,
    Timeout:       5 * time.Second,
    Transport: &http.Transport{
        Proxy: http.ProxyFromEnvironment,
        TLSClientConfig: &tls.Config{
            InsecureSkipVerify: true,   // GOINSECURE 时跳过证书校验
        },
    },
}

// 文件: cmd/go/internal/web/http.go:133-135  选用条件
var client *http.Client
if security == Insecure && url.Scheme == "https" {
    client = impatientInsecureHTTPClient
}

调用链分析

层次 函数 文件:行号 角色
1 web.Get / web.get cmd/go/internal/web/http.go:76 入口,按 security 模式分发
2 fetch cmd/go/internal/web/http.go:119 构造请求
3 client 选择 cmd/go/internal/web/http.go:134 Insecure+https 选 insecure client
4 impatientInsecureHTTPClient.Do cmd/go/internal/web/http.go:37,159 InsecureSkipVerify 生效

修复建议

// 1) 用户侧:将 GOINSECURE 限定到明确的内网主机前缀,避免 GOINSECURE=*。
//    例如:GOINSECURE=git.internal.corp.com,*.corp.local
// 2) 内网自签证书场景优先用 GOAUTH 或自定义 CA 而非 GOINSECURE。
// 3) 可在 -x 输出中当 insecure client 被启用时打印安全告警,提醒用户。

🟢 [建议] F5 — 模块拉取网络响应体无界 io.ReadAll(潜在内存耗尽 DoS)

CWE: CWE-400(不受控的资源消耗)、CWE-770(资源分配无限制)
规则: R05-resource-limit / R01-input-validation
描述: cmd/go/internal/modfetch/proxy.go 的 getBytes 与 cmd/go/internal/web/api.go 的 GetBytes 均对 HTTP 响应体直接调用 io.ReadAll(body) 而无显式字节数上限。这些响应来自 GOPROXY 代理或 go-import meta 服务器。安全保留的默认 client(securityPreservingDefaultClient,基于 http.DefaultClient)未设置 Client.Timeout,因此既无大小上限也无整体读超时。
影响: 恶意或被入侵的 GOPROXY / 模块服务器可流式返回超大响应体,导致 go 命令进程内存耗尽(OOM)而崩溃,构成对构建/CI 环境的拒绝服务。net/http/request.go:1278-1293 的表单解析有 10MB 上限作为正面对照,而此处网络拉取无对应防护。
CVSS: 4.3(Medium)— 需攻击者控制或能影响用户配置的 GOPROXY/模块服务器;用户侧影响为构建 DoS。
数据来源: GOPROXY 服务器 / go-import meta 服务器的 HTTP 响应体
风险传播路径: go get / go mod download → proxyRepo.getBytes → io.ReadAll(body) → 无上限分配 → OOM。

调用栈

go mod download / modfetch.proxyRepo.*         [cmd/go/internal/modfetch/proxy.go]
  -> proxyRepo.getBytes(ctx, path)             [cmd/go/internal/modfetch/proxy.go:259]
     -> p.getBody(ctx, path) -> web.Get        [cmd/go/internal/modfetch/proxy.go:275]
     -> io.ReadAll(body)                       [cmd/go/internal/modfetch/proxy.go:266  无上限]
// 另一路径
web.GetBytes(u)                                [cmd/go/internal/web/api.go:84]
  -> Get(DefaultSecurity, u)                   [cmd/go/internal/web/api.go:85]
  -> io.ReadAll(resp.Body)                     [cmd/go/internal/web/api.go:93  无上限]

问题代码

// 文件: cmd/go/internal/modfetch/proxy.go:259-273
func (p *proxyRepo) getBytes(ctx context.Context, path string) ([]byte, error) {
    body, redactedURL, err := p.getBody(ctx, path)
    if err != nil {
        return nil, err
    }
    defer body.Close()
    b, err := io.ReadAll(body)   // 无 LimitReader,无字节数上限
    if err != nil {
        return b, &url.Error{Op: "read", URL: redactedURL, Err: err}
    }
    return b, nil
}

// 文件: cmd/go/internal/web/api.go:84-96
func GetBytes(u *url.URL) ([]byte, error) {
    resp, err := Get(DefaultSecurity, u)
    // ...
    b, err := io.ReadAll(resp.Body)   // 无上限
    // ...
}

调用链分析

层次 函数 文件:行号 角色
1 modfetch.proxyRepo.getBytes / web.GetBytes proxy.go:259 / api.go:84 入口,拉取模块元数据
2 web.Get / p.getBody proxy.go:275 / api.go:85 发起 HTTP 请求
3 io.ReadAll(body) proxy.go:266 / api.go:93 无界读取响应体

修复建议

// 为模块元数据拉取设置合理的字节数上限(如 32MB,模块 .mod/.info/.zip 元数据远小于此),
// 并为默认安全 client 设置整体超时。
const maxModuleMetadataBytes = 32 << 20 // 32 MB
b, err := io.ReadAll(io.LimitReader(body, maxModuleMetadataBytes+1))
if err != nil { /* ... */ }
if int64(len(b)) > maxModuleMetadataBytes {
    return nil, fmt.Errorf("module metadata at %s too large (max %d bytes)", redactedURL, maxModuleMetadataBytes)
}

// 同时为 securityPreservingDefaultClient 设置 Timeout:
var securityPreservingDefaultClient = securityPreservingHTTPClient(&http.Client{
    Timeout: 60 * time.Second,
})

⚠️ 疑似[建议] F6 — buildCommand 对空白字符 GOAUTH 值发生 panic(健壮性/空索引)

CWE: CWE-476(空指针/空切片解引用)、CWE-754(异常条件检查不当)
规则: R05-error-handling / R01-input-validation
置信度: 85%
描述: cmd/go/internal/auth/userauth.go 的 buildCommand 调用 quoted.Split(command) 后直接 exec.Command(words[0], words[1:]...)。quoted.Split(cmd/internal/quoted/quoted.go:25-59)对空白字符-only 输入(如 " ")会返回 (nil, nil)——即切片为空且无错误:循环中先 trim 掉全部空白,len(s)==0 即 break,f 保持 nil。此时 words[0] 对 nil 切片取下标 0 触发 index out of range panic。调用方 runAuthCommand 在 line 24 仅有 if command == "" 判空,无法捕获空白字符-only 的 GOAUTH 值(" " != ""),故 panic 不会被前置检查拦截。
影响: 用户设置 GOAUTH=" "(或因配置脚本拼接错误产生纯空白值)时,go 命令在发起认证时崩溃(panic),而非返回清晰错误。属本地可用性/健壮性问题,非安全漏洞(GOAUTH 由用户自设,攻击者需已能修改用户环境)。
CVSS: 2.0(Low)— 本地 DoS,需用户自设异常 GOAUTH。
数据来源: GOAUTH 环境变量(用户配置)
风险传播路径: cfg.GOAUTH 解析 → runAuthCommand(" ", url, res) → command == "" 为 false(通过) → buildCommand(" ") → quoted.Split(" ") → (nil, nil) → exec.Command(words[0]...) → panic: runtime error: index out of range。

调用栈

runAuthCommand(command="   ", url, res)     [cmd/go/internal/auth/userauth.go:23]
  -> if command == ""  (false, 不拦截)       [cmd/go/internal/auth/userauth.go:24]
  -> buildCommand(command)                   [cmd/go/internal/auth/userauth.go:27]
     -> quoted.Split("   ") -> (nil, nil)    [cmd/internal/quoted/quoted.go:25  返回空切片无错误]
     -> exec.Command(words[0], words[1:]...) [cmd/go/internal/auth/userauth.go:113  panic: index out of range]

问题代码

// 文件: cmd/go/internal/auth/userauth.go:108-115
func buildCommand(command string) (*exec.Cmd, error) {
    words, err := quoted.Split(command)
    if err != nil {
        return nil, fmt.Errorf("cannot parse GOAUTH command %s: %v", command, err)
    }
    cmd := exec.Command(words[0], words[1:]...)  // words 为 nil 时 panic
    return cmd, nil
}

// 文件: cmd/go/internal/auth/userauth.go:23-30  调用方仅判 == ""
func runAuthCommand(command string, url string, res *http.Response) (map[string,http.Header, error) {
    if command == "" {                          // 无法捕获 "   "
        panic("GOAUTH invoked an empty authenticator command:" + command)
    }
    cmd, err := buildCommand(command)
    // ...
}

// 文件: cmd/internal/quoted/quoted.go:25-58  空白输入返回 (nil, nil)
func Split(s string) ([]string, error) {
    var f []string
    for len(s) > 0 {
        for len(s) > 0 && isSpaceByte(s[0]) { s = s[1:] }  // "   " 全被 trim
        if len(s) == 0 { break }                            // 退出,f 仍为 nil
        // ...
    }
    return f, nil   // (nil, nil)
}

调用链分析

层次 函数 文件:行号 角色
1 runAuthCommand cmd/go/internal/auth/userauth.go:23 入口,仅判 ==""
2 buildCommand cmd/go/internal/auth/userauth.go:108 解析命令词
3 quoted.Split cmd/internal/quoted/quoted.go:25 空白输入返回 (nil,nil)
4 exec.Command(words[0], ...) cmd/go/internal/auth/userauth.go:113 nil[0] panic

修复建议

// 在 buildCommand 中显式校验 words 非空,返回清晰错误而非 panic:
func buildCommand(command string) (*exec.Cmd, error) {
    words, err := quoted.Split(command)
    if err != nil {
        return nil, fmt.Errorf("cannot parse GOAUTH command %s: %v", command, err)
    }
    if len(words) == 0 {
        return nil, fmt.Errorf("GOAUTH command %q contains no executable", command)
    }
    cmd := exec.Command(words[0], words[1:]...)
    return cmd, nil
}

// 同时建议 runAuthCommand 将 =="" 收紧为 strings.TrimSpace(command) == ""。

⚠️ 疑似[建议] F7 — VCS 命令执行面:模块路径派生的参数注入风险(已缓解,残留风险)

CWE: CWE-78(操作系统命令注入)、CWE-88(参数注入)
规则: R02-command-injection
置信度: 70%(已缓解,残留风险)
描述: cmd/go/internal/vcs/vcs.go 的 run1 通过 exec.Command(v.Cmd, args...) 执行 VCS 二进制(git/hg/svn 等)。args 由 cmdline 模板经 strings.Fields 拆分并 expand(m, arg) 替换占位符得到,占位符的值(scheme、repo 等)源自模块 import 路径。Shell 注入不可能(argv 数组,不经 shell)。参数注入风险(如 --upload-pack=恶意命令)由上游 module.CheckPath/module.EscapePath 路径校验 + git 命令中的 --end-of-options 守卫(git.go 中 8 处)缓解。但 VCS 子进程参数解析行为各异,且此处是历史高危面(旧版 go get CVE-2018-6574 即在此类路径),未来若路径校验或 VCS 模板出现回归,可能重新引入参数注入。
影响: 在当前 Go 1.24 实现下,路径校验与 --end-of-options 已有效收敛参数注入;本条记录该攻击面与现有缓解,提示后续维护关注。
CVSS: 3.1(Low)— 需路径校验/VCS 模板出现回归方可利用。
数据来源: 模块 import 路径 / VCS URL(go get 输入)
风险传播路径: go get <module> → vcsCmd.run/Ping → run1(dir, cmdline, keyval) → expand(m, arg)(m 含 repo/scheme) → exec.Command(v.Cmd, args...) → VCS 二进制解析参数。

调用栈

go get <import-path>                            [cmd/go/internal/modget/get.go]
  -> vcsCmd.Create / vcsCmd.Download            [cmd/go/internal/vcs/vcs.go]
     -> v.Cmd.run(dir, cmdline, "scheme", scheme, "repo", repo)  [cmd/go/internal/vcs/vcs.go:Ping/run]
        -> v.run1(dir, cmdline, keyval, verbose)                 [cmd/go/internal/vcs/vcs.go:451]
           -> args := strings.Fields(cmdline); expand(m, arg)    [cmd/go/internal/vcs/vcs.go:456-459]
           -> exec.Command(v.Cmd, args...)                       [cmd/go/internal/vcs/vcs.go:491]
              -> cmd.Output()                                    [cmd/go/internal/vcs/vcs.go:500]

问题代码

// 文件: cmd/go/internal/vcs/vcs.go:451-500
func (v *Cmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) {
    m := make(map[string]string)
    for i := 0; i < len(keyval); i += 2 {
        m[keyval[i]] = keyval[i+1]              // repo/scheme 等源自 import 路径
    }
    args := strings.Fields(cmdline)
    for i, arg := range args {
        args[i] = expand(m, arg)                // 占位符替换为路径派生值
    }
    // ... --go-internal-mkdir / --go-internal-cd 处理 ...
    cmd := exec.Command(v.Cmd, args...)         // 执行 VCS 二进制
    cmd.Dir = dir
    // ...
    out, err := cmd.Output()
    return out, err
}

// 文件: cmd/go/internal/vcs/vcs.go:515-533  Ping 注入 repo
func (v *Cmd) Ping(scheme, repo string) error {
    // ...
    return v.runVerboseOnly(dir, v.PingCmd, "scheme", scheme, "repo", repo)
}

调用链分析

层次 函数 文件:行号 角色
1 vcsCmd.Create/Download/Ping cmd/go/internal/vcs/vcs.go go get 入口
2 v.Cmd.run / runVerboseOnly cmd/go/internal/vcs/vcs.go:run 传递 repo/scheme
3 run1 cmd/go/internal/vcs/vcs.go:451 模板展开
4 expand(m, arg) cmd/go/internal/vcs/vcs.go:458 路径派生值填入参数
5 exec.Command(v.Cmd, args...) cmd/go/internal/vcs/vcs.go:491 执行 VCS

修复建议

// 1) 维持并强化 module.CheckPath / module.EscapePath 对 repo/scheme 的前置校验
//    (拒绝含 "--"、控制字符、空格的路径段),确保进入 expand 的值不可能以 "-" 开头。
// 2) 在 run1 中对展开后的 args 做防御性校验:任何以 "-" 开头且非已知 VCS 选项的 token
//    一律拒绝,或对 git 类命令统一在 rev 前插入 --end-of-options。
// 3) 持续回归测试:覆盖 import 路径含 "--upload-pack=..."、"-"、空格等畸形用例。
// 4) 应用层:在不可信 import 路径场景限制 GOPROXY=off / GOFLAGS=-mod=readonly 降低动态拉取面。

【缺陷所属的os版本】

24.03-LTS-next

【内核版本】

kernel6.6

【缺陷所属软件及版本号】

go

【环境信息】

【问题复现步骤】

【实际结果】

【期望结果】

【其他相关附件信息】

【缺陷详情及分析指导参考链接】

二、缺陷分析结构反馈
影响性分析说明:

缺陷严重等级:(Critical/High/Moderate/Low)

缺陷根因说明:

受影响版本排查(受影响/不受影响):
openEuler-20.03-LTS-SP4
openEuler-22.03-LTS-SP4
openEuler-24.03-LTS-SP1
openEuler-24.03-LTS-SP3
openEuler-24.03-LTS-SP4

修复是否涉及abi变化(是/否):
openEuler-20.03-LTS-SP4
openEuler-22.03-LTS-SP4
openEuler-24.03-LTS-SP1
openEuler-24.03-LTS-SP3
openEuler-24.03-LTS-SP4

likedislike
openeuler-ci-botopeneuler-ci-bot成员
19 天前 将 fuowang 设为负责人
openeuler-ci-botopeneuler-ci-bot成员
19 天前 修改了issue 的描述
openeuler-ci-botopeneuler-ci-bot成员
19 天前 将 genedna、jing-rui、yangna1310 设为负责人
openeuler-ci-botopeneuler-ci-bot成员
19 天前 添加了label:sig/sig-golang
openeuler-ci-bot
openeuler-ci-bot成员
19 天前 评论:

以下的要求不是强制性的, 未按模板评论时对issue无任何影响
issue处理注意事项:
1. 当前issue受影响的分支提交pr时, 须在pr描述中填写当前issue编号进行关联, 否则无法关闭当前issue;
2. 模板内容需要填写完整, 无论是受影响或者不受影响都需要填写完整内容,未引入的分支不需要填写, 否则无法关闭当前issue;
3. 以下为模板中需要填写完整的内容, 请复制到评论区回复, 注: 内容的标题名称(影响性分析说明, 缺陷严重等级, 受影响版本排查(受影响/不受影响), 修复是否涉及abi变化(是/否))不能省略,省略后defect-manager将无法正常解析填写内容.
评论区可能使用到的指令说明:

指令 指令说明 使用权限
/check-issue 触发defect-manager校验 不限
/reason xxx /reason +挂起或取消条件 不限

影响性分析说明:

缺陷严重等级:(Critical/High/Moderate/Low)

缺陷根因说明:

受影响版本排查(受影响/不受影响):

  1. openEuler-20.03-LTS-SP4:
  2. openEuler-22.03-LTS-SP4:
  3. openEuler-24.03-LTS-SP1:
  4. openEuler-24.03-LTS-SP3:
  5. openEuler-24.03-LTS-SP4:

abi变化(是/否):

  1. openEuler-20.03-LTS-SP4:
  2. openEuler-22.03-LTS-SP4:
  3. openEuler-24.03-LTS-SP1:
  4. openEuler-24.03-LTS-SP3:
  5. openEuler-24.03-LTS-SP4:

缺陷issue处理具体操作请参考:
https://atomgit.com/openeuler/cve-manager/blob/master/cve-vulner-manager/doc/md/defect-manager-manual.md
pr关联issue具体操作请参考:
https://docs.atomgit.com/docs/help/home/org_project/pullrequests/pr-related-issue

likedislike
openeuler-ci-bot
openeuler-ci-bot成员
19 天前 评论:

Welcome To openEuler Community

Hey @yangna1310 , thanks for your contribution to the community.

Bot Usage Manual

I'm the Bot here serving you. You can find the instructions on how to interact with me at Here . That means you can comment below every pull request or issue to trigger Bot Commands. You can self-configure the PR merge rules for this repository. For more details, please refer to Here.

Contact Guide

If you have any questions, please contact the SIG: sig-golang ,
and any of the maintainers: @genedna, @jing-rui ,
and any of the committers: @fuowang, @wd-gitcode .

likedislike
openeuler-ci-botopeneuler-ci-bot成员
19 天前 添加了label:DEFECT/UNFIXED