mirror of
https://github.com/fatedier/frp.git
synced 2026-04-18 15:09:10 +08:00
Compare commits
2 Commits
d667be7a0a
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8dfd6efcc | ||
|
|
a9a4416ecf |
@@ -1 +1,3 @@
|
||||
## Features
|
||||
## Fixes
|
||||
|
||||
* Fixed a configuration-dependent authentication bypass in `type = "http"` proxies when `routeByHTTPUser` is used together with `httpUser` / `httpPassword`. This affected proxy-style requests. Proxy-style authentication failures now return `407 Proxy Authentication Required`.
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
package version
|
||||
|
||||
var version = "0.68.0"
|
||||
var version = "0.68.1"
|
||||
|
||||
func Full() string {
|
||||
return version
|
||||
|
||||
@@ -187,16 +187,25 @@ func (rp *HTTPReverseProxy) CreateConnection(reqRouteInfo *RequestRouteInfo, byE
|
||||
return nil, fmt.Errorf("%v: %s %s %s", ErrNoRouteFound, host, reqRouteInfo.URL, reqRouteInfo.HTTPUser)
|
||||
}
|
||||
|
||||
func (rp *HTTPReverseProxy) CheckAuth(domain, location, routeByHTTPUser, user, passwd string) bool {
|
||||
vr, ok := rp.getVhost(domain, location, routeByHTTPUser)
|
||||
if ok {
|
||||
checkUser := vr.payload.(*RouteConfig).Username
|
||||
checkPasswd := vr.payload.(*RouteConfig).Password
|
||||
if (checkUser != "" || checkPasswd != "") && (checkUser != user || checkPasswd != passwd) {
|
||||
func checkRouteAuthByRequest(req *http.Request, rc *RouteConfig) bool {
|
||||
if rc == nil {
|
||||
return true
|
||||
}
|
||||
if rc.Username == "" && rc.Password == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
if req.URL.Host != "" {
|
||||
proxyAuth := req.Header.Get("Proxy-Authorization")
|
||||
if proxyAuth == "" {
|
||||
return false
|
||||
}
|
||||
user, passwd, ok := httppkg.ParseBasicAuth(proxyAuth)
|
||||
return ok && user == rc.Username && passwd == rc.Password
|
||||
}
|
||||
return true
|
||||
|
||||
user, passwd, ok := req.BasicAuth()
|
||||
return ok && user == rc.Username && passwd == rc.Password
|
||||
}
|
||||
|
||||
// getVhost tries to get vhost router by route policy.
|
||||
@@ -266,18 +275,25 @@ func (rp *HTTPReverseProxy) connectHandler(rw http.ResponseWriter, req *http.Req
|
||||
go libio.Join(remote, client)
|
||||
}
|
||||
|
||||
func (rp *HTTPReverseProxy) injectRequestInfoToCtx(req *http.Request) *http.Request {
|
||||
user := ""
|
||||
// If url host isn't empty, it's a proxy request. Get http user from Proxy-Authorization header.
|
||||
func getRequestRouteUser(req *http.Request) string {
|
||||
if req.URL.Host != "" {
|
||||
proxyAuth := req.Header.Get("Proxy-Authorization")
|
||||
if proxyAuth != "" {
|
||||
user, _, _ = httppkg.ParseBasicAuth(proxyAuth)
|
||||
if proxyAuth == "" {
|
||||
// Preserve legacy proxy-mode routing when clients send only Authorization,
|
||||
// so requests still hit the matched route and return 407 instead of 404.
|
||||
// Auth validation intentionally does not share this fallback.
|
||||
user, _, _ := req.BasicAuth()
|
||||
return user
|
||||
}
|
||||
user, _, _ := httppkg.ParseBasicAuth(proxyAuth)
|
||||
return user
|
||||
}
|
||||
if user == "" {
|
||||
user, _, _ = req.BasicAuth()
|
||||
}
|
||||
user, _, _ := req.BasicAuth()
|
||||
return user
|
||||
}
|
||||
|
||||
func (rp *HTTPReverseProxy) injectRequestInfoToCtx(req *http.Request) *http.Request {
|
||||
user := getRequestRouteUser(req)
|
||||
|
||||
reqRouteInfo := &RequestRouteInfo{
|
||||
URL: req.URL.Path,
|
||||
@@ -297,16 +313,19 @@ func (rp *HTTPReverseProxy) injectRequestInfoToCtx(req *http.Request) *http.Requ
|
||||
}
|
||||
|
||||
func (rp *HTTPReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
domain, _ := httppkg.CanonicalHost(req.Host)
|
||||
location := req.URL.Path
|
||||
user, passwd, _ := req.BasicAuth()
|
||||
if !rp.CheckAuth(domain, location, user, user, passwd) {
|
||||
rw.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
||||
http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
newreq := rp.injectRequestInfoToCtx(req)
|
||||
rc := newreq.Context().Value(RouteConfigKey).(*RouteConfig)
|
||||
if !checkRouteAuthByRequest(req, rc) {
|
||||
if req.URL.Host != "" {
|
||||
rw.Header().Set("Proxy-Authenticate", `Basic realm="Restricted"`)
|
||||
http.Error(rw, http.StatusText(http.StatusProxyAuthRequired), http.StatusProxyAuthRequired)
|
||||
} else {
|
||||
rw.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
||||
http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
newreq := rp.injectRequestInfoToCtx(req)
|
||||
if req.Method == http.MethodConnect {
|
||||
rp.connectHandler(rw, newreq)
|
||||
} else {
|
||||
|
||||
102
pkg/util/vhost/http_test.go
Normal file
102
pkg/util/vhost/http_test.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package vhost
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
httppkg "github.com/fatedier/frp/pkg/util/http"
|
||||
)
|
||||
|
||||
func TestCheckRouteAuthByRequest(t *testing.T) {
|
||||
rc := &RouteConfig{
|
||||
Username: "alice",
|
||||
Password: "secret",
|
||||
}
|
||||
|
||||
t.Run("accepts nil route config", func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
require.True(t, checkRouteAuthByRequest(req, nil))
|
||||
})
|
||||
|
||||
t.Run("accepts route without credentials", func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
require.True(t, checkRouteAuthByRequest(req, &RouteConfig{}))
|
||||
})
|
||||
|
||||
t.Run("accepts authorization header", func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.SetBasicAuth("alice", "secret")
|
||||
require.True(t, checkRouteAuthByRequest(req, rc))
|
||||
})
|
||||
|
||||
t.Run("accepts proxy authorization header", func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "http://target.example.com/", nil)
|
||||
req.Header.Set("Proxy-Authorization", httppkg.BasicAuth("alice", "secret"))
|
||||
require.True(t, checkRouteAuthByRequest(req, rc))
|
||||
})
|
||||
|
||||
t.Run("rejects authorization fallback for proxy request", func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "http://target.example.com/", nil)
|
||||
req.SetBasicAuth("alice", "secret")
|
||||
require.False(t, checkRouteAuthByRequest(req, rc))
|
||||
})
|
||||
|
||||
t.Run("rejects wrong proxy authorization even when authorization matches", func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "http://target.example.com/", nil)
|
||||
req.SetBasicAuth("alice", "secret")
|
||||
req.Header.Set("Proxy-Authorization", httppkg.BasicAuth("alice", "wrong"))
|
||||
require.False(t, checkRouteAuthByRequest(req, rc))
|
||||
})
|
||||
|
||||
t.Run("rejects when neither header matches", func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "http://target.example.com/", nil)
|
||||
req.SetBasicAuth("alice", "wrong")
|
||||
req.Header.Set("Proxy-Authorization", httppkg.BasicAuth("alice", "wrong"))
|
||||
require.False(t, checkRouteAuthByRequest(req, rc))
|
||||
})
|
||||
|
||||
t.Run("rejects proxy authorization on direct request", func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.Header.Set("Proxy-Authorization", httppkg.BasicAuth("alice", "secret"))
|
||||
require.False(t, checkRouteAuthByRequest(req, rc))
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetRequestRouteUser(t *testing.T) {
|
||||
t.Run("proxy request uses proxy authorization username", func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "http://target.example.com/", nil)
|
||||
req.Host = "target.example.com"
|
||||
req.Header.Set("Proxy-Authorization", httppkg.BasicAuth("proxy-user", "proxy-pass"))
|
||||
req.SetBasicAuth("direct-user", "direct-pass")
|
||||
|
||||
require.Equal(t, "proxy-user", getRequestRouteUser(req))
|
||||
})
|
||||
|
||||
t.Run("connect request keeps proxy authorization routing", func(t *testing.T) {
|
||||
req := httptest.NewRequest("CONNECT", "http://target.example.com:443", nil)
|
||||
req.Host = "target.example.com:443"
|
||||
req.Header.Set("Proxy-Authorization", httppkg.BasicAuth("proxy-user", "proxy-pass"))
|
||||
req.SetBasicAuth("direct-user", "direct-pass")
|
||||
|
||||
require.Equal(t, "proxy-user", getRequestRouteUser(req))
|
||||
})
|
||||
|
||||
t.Run("direct request uses authorization username", func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.Host = "example.com"
|
||||
req.SetBasicAuth("direct-user", "direct-pass")
|
||||
|
||||
require.Equal(t, "direct-user", getRequestRouteUser(req))
|
||||
})
|
||||
|
||||
t.Run("proxy request does not fall back when proxy authorization is invalid", func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "http://target.example.com/", nil)
|
||||
req.Host = "target.example.com"
|
||||
req.Header.Set("Proxy-Authorization", "Basic !!!")
|
||||
req.SetBasicAuth("direct-user", "direct-pass")
|
||||
|
||||
require.Empty(t, getRequestRouteUser(req))
|
||||
})
|
||||
}
|
||||
@@ -144,6 +144,79 @@ var _ = ginkgo.Describe("[Feature: HTTP]", func() {
|
||||
Ensure()
|
||||
})
|
||||
|
||||
ginkgo.It("HTTP proxy mode uses proxy auth consistently", func() {
|
||||
vhostHTTPPort := f.AllocPort()
|
||||
serverConf := getDefaultServerConf(vhostHTTPPort)
|
||||
|
||||
backendPort := f.AllocPort()
|
||||
f.RunServer("", newHTTPServer(backendPort, "PRIVATE"))
|
||||
|
||||
clientConf := consts.DefaultClientConfig
|
||||
clientConf += fmt.Sprintf(`
|
||||
[[proxies]]
|
||||
name = "protected"
|
||||
type = "http"
|
||||
localPort = %d
|
||||
customDomains = ["normal.example.com"]
|
||||
routeByHTTPUser = "alice"
|
||||
httpUser = "alice"
|
||||
httpPassword = "secret"
|
||||
`, backendPort)
|
||||
|
||||
f.RunProcesses(serverConf, []string{clientConf})
|
||||
|
||||
proxyURLWithAuth := func(username, password string) string {
|
||||
if username == "" {
|
||||
return fmt.Sprintf("http://127.0.0.1:%d", vhostHTTPPort)
|
||||
}
|
||||
return fmt.Sprintf("http://%s:%s@127.0.0.1:%d", username, password, vhostHTTPPort)
|
||||
}
|
||||
|
||||
framework.NewRequestExpect(f).Explain("direct no auth").Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("normal.example.com")
|
||||
}).
|
||||
Ensure(framework.ExpectResponseCode(http.StatusNotFound))
|
||||
|
||||
framework.NewRequestExpect(f).Explain("direct correct auth").Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("normal.example.com").HTTPAuth("alice", "secret")
|
||||
}).
|
||||
ExpectResp([]byte("PRIVATE")).
|
||||
Ensure()
|
||||
|
||||
framework.NewRequestExpect(f).Explain("direct wrong auth").Port(vhostHTTPPort).
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().HTTPHost("normal.example.com").HTTPAuth("alice", "wrong")
|
||||
}).
|
||||
Ensure(framework.ExpectResponseCode(http.StatusUnauthorized))
|
||||
|
||||
framework.NewRequestExpect(f).Explain("proxy correct proxy auth").
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().Addr("normal.example.com").Proxy(proxyURLWithAuth("alice", "secret"))
|
||||
}).
|
||||
ExpectResp([]byte("PRIVATE")).
|
||||
Ensure()
|
||||
|
||||
framework.NewRequestExpect(f).Explain("proxy wrong proxy auth").
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().Addr("normal.example.com").Proxy(proxyURLWithAuth("alice", "wrong"))
|
||||
}).
|
||||
Ensure(framework.ExpectResponseCode(http.StatusProxyAuthRequired))
|
||||
|
||||
framework.NewRequestExpect(f).Explain("proxy request ignores authorization header").
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().Addr("normal.example.com").Proxy(proxyURLWithAuth("", "")).HTTPAuth("alice", "secret")
|
||||
}).
|
||||
Ensure(framework.ExpectResponseCode(http.StatusProxyAuthRequired))
|
||||
|
||||
framework.NewRequestExpect(f).Explain("proxy wrong proxy auth with correct authorization").
|
||||
RequestModify(func(r *request.Request) {
|
||||
r.HTTP().Addr("normal.example.com").Proxy(proxyURLWithAuth("alice", "wrong")).HTTPAuth("alice", "secret")
|
||||
}).
|
||||
Ensure(framework.ExpectResponseCode(http.StatusProxyAuthRequired))
|
||||
})
|
||||
|
||||
ginkgo.It("HTTP Basic Auth", func() {
|
||||
vhostHTTPPort := f.AllocPort()
|
||||
serverConf := getDefaultServerConf(vhostHTTPPort)
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import ClientConfigure from '../views/ClientConfigure.vue'
|
||||
import ProxyDetail from '../views/ProxyDetail.vue'
|
||||
import ProxyEdit from '../views/ProxyEdit.vue'
|
||||
import ProxyList from '../views/ProxyList.vue'
|
||||
import VisitorDetail from '../views/VisitorDetail.vue'
|
||||
import VisitorEdit from '../views/VisitorEdit.vue'
|
||||
import VisitorList from '../views/VisitorList.vue'
|
||||
import { useProxyStore } from '../stores/proxy'
|
||||
|
||||
const router = createRouter({
|
||||
@@ -15,12 +19,12 @@ const router = createRouter({
|
||||
{
|
||||
path: '/proxies',
|
||||
name: 'ProxyList',
|
||||
component: () => import('../views/ProxyList.vue'),
|
||||
component: ProxyList,
|
||||
},
|
||||
{
|
||||
path: '/proxies/detail/:name',
|
||||
name: 'ProxyDetail',
|
||||
component: () => import('../views/ProxyDetail.vue'),
|
||||
component: ProxyDetail,
|
||||
},
|
||||
{
|
||||
path: '/proxies/create',
|
||||
@@ -37,12 +41,12 @@ const router = createRouter({
|
||||
{
|
||||
path: '/visitors',
|
||||
name: 'VisitorList',
|
||||
component: () => import('../views/VisitorList.vue'),
|
||||
component: VisitorList,
|
||||
},
|
||||
{
|
||||
path: '/visitors/detail/:name',
|
||||
name: 'VisitorDetail',
|
||||
component: () => import('../views/VisitorDetail.vue'),
|
||||
component: VisitorDetail,
|
||||
},
|
||||
{
|
||||
path: '/visitors/create',
|
||||
|
||||
142
web/package-lock.json
generated
142
web/package-lock.json
generated
@@ -640,11 +640,10 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@eslint/config-array/node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -721,11 +720,10 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -1337,11 +1335,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@@ -1405,11 +1402,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -2255,11 +2251,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/language-core/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -2572,11 +2567,10 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
|
||||
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
@@ -3105,11 +3099,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/defu": {
|
||||
"version": "6.1.4",
|
||||
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
|
||||
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"version": "6.1.7",
|
||||
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz",
|
||||
"integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/destr": {
|
||||
"version": "2.0.5",
|
||||
@@ -3755,11 +3748,10 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/eslint/node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -4998,16 +4990,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
||||
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
|
||||
"license": "MIT"
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
||||
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz",
|
||||
"integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==",
|
||||
"license": "MIT"
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",
|
||||
"integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="
|
||||
},
|
||||
"node_modules/lodash-unified": {
|
||||
"version": "1.0.3",
|
||||
@@ -5274,11 +5264,10 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/npm-run-all/node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -5686,11 +5675,10 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
@@ -6735,11 +6723,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -6965,11 +6952,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/unctx/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -7084,11 +7070,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/unimport/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -7153,11 +7138,10 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unplugin-auto-import/node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
@@ -7258,11 +7242,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin-element-plus/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -7344,11 +7327,10 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unplugin-vue-components/node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
@@ -7479,11 +7461,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
|
||||
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz",
|
||||
"integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.27.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -7586,11 +7567,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user