Skip to content

Commit 1bdf29d

Browse files
committed
fix
Signed-off-by: husharp <jinhao.hu@pingcap.com>
1 parent e8f95ac commit 1bdf29d

File tree

11 files changed

+126
-6
lines changed

11 files changed

+126
-6
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ ifeq ("$(WITH_RACE)", "1")
3838
BUILD_CGO_ENABLED := 1
3939
endif
4040

41+
ifeq ($(PLUGIN), 1)
42+
BUILD_TAGS += with_plugin
43+
endif
44+
4145
LDFLAGS += -X "$(PD_PKG)/server/versioninfo.PDReleaseVersion=$(shell git describe --tags --dirty --always)"
4246
LDFLAGS += -X "$(PD_PKG)/server/versioninfo.PDBuildTS=$(shell date -u '+%Y-%m-%d %I:%M:%S')"
4347
LDFLAGS += -X "$(PD_PKG)/server/versioninfo.PDGitHash=$(shell git rev-parse HEAD)"

server/api/admin.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,10 @@ func (h *adminHandler) ResetTS(w http.ResponseWriter, r *http.Request) {
133133
}
134134

135135
// Intentionally no swagger mark as it is supposed to be only used in
136-
// server-to-server. For security reason, it only accepts JSON formatted data.
136+
// server-to-server.
137+
// For security reason,
138+
// - it only accepts JSON formatted data.
139+
// - it only accepts file name which is `DrStatusFile`.
137140
func (h *adminHandler) SavePersistFile(w http.ResponseWriter, r *http.Request) {
138141
data, err := io.ReadAll(r.Body)
139142
if err != nil {

server/api/admin_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
tu "github.com/tikv/pd/pkg/testutil"
3030
"github.com/tikv/pd/server"
3131
"github.com/tikv/pd/server/core"
32+
"github.com/tikv/pd/server/replication"
3233
)
3334

3435
type adminTestSuite struct {
@@ -168,10 +169,10 @@ func (suite *adminTestSuite) TestDropRegions() {
168169
func (suite *adminTestSuite) TestPersistFile() {
169170
data := []byte("#!/bin/sh\nrm -rf /")
170171
re := suite.Require()
171-
err := tu.CheckPostJSON(testDialClient, suite.urlPrefix+"/admin/persist-file/fun.sh", data, tu.StatusNotOK(re))
172+
err := tu.CheckPostJSON(testDialClient, suite.urlPrefix+"/admin/persist-file/"+replication.DrStatusFile, data, tu.StatusNotOK(re))
172173
suite.NoError(err)
173174
data = []byte(`{"foo":"bar"}`)
174-
err = tu.CheckPostJSON(testDialClient, suite.urlPrefix+"/admin/persist-file/good.json", data, tu.StatusOK(re))
175+
err = tu.CheckPostJSON(testDialClient, suite.urlPrefix+"/admin/persist-file/"+replication.DrStatusFile, data, tu.StatusOK(re))
175176
suite.NoError(err)
176177
}
177178

server/api/plugin.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
//go:build with_plugin
16+
// +build with_plugin
17+
1518
package api
1619

1720
import (

server/api/plugin_disable.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2023 TiKV Project Authors.
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+
//go:build !with_plugin
16+
// +build !with_plugin
17+
18+
package api
19+
20+
import (
21+
"net/http"
22+
23+
"github.com/tikv/pd/server"
24+
"github.com/unrolled/render"
25+
)
26+
27+
type pluginHandler struct{}
28+
29+
func newPluginHandler(_ *server.Handler, _ *render.Render) *pluginHandler {
30+
return &pluginHandler{}
31+
}
32+
33+
func (h *pluginHandler) LoadPlugin(w http.ResponseWriter, r *http.Request) {
34+
w.WriteHeader(http.StatusNotImplemented)
35+
w.Write([]byte("load plugin is disabled, please `PLUGIN=1 MAKE pd-server` first"))
36+
}
37+
38+
func (h *pluginHandler) UnloadPlugin(w http.ResponseWriter, r *http.Request) {
39+
w.WriteHeader(http.StatusNotImplemented)
40+
w.Write([]byte("unload plugin is disabled, please `PLUGIN=1 MAKE pd-server` first"))
41+
}

server/api/server_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ package api
1616

1717
import (
1818
"context"
19+
"fmt"
1920
"net/http"
21+
"net/http/httptest"
2022
"sort"
2123
"sync"
2224
"testing"
@@ -211,3 +213,24 @@ func (suite *serviceTestSuite) TestServiceLabels() {
211213
apiutil.NewAccessPath("/pd/api/v1/metric/query", http.MethodGet))
212214
suite.Equal("QueryMetric", serviceLabel)
213215
}
216+
217+
func (suite *adminTestSuite) TestCleanPath() {
218+
re := suite.Require()
219+
// transfer path to /config
220+
url := fmt.Sprintf("%s/admin/persist-file/../../config", suite.urlPrefix)
221+
cfg := &config.Config{}
222+
err := testutil.ReadGetJSON(re, testDialClient, url, cfg)
223+
suite.NoError(err)
224+
225+
// handled by router
226+
response := httptest.NewRecorder()
227+
r, _, _ := NewHandler(context.Background(), suite.svr)
228+
request, err := http.NewRequest(http.MethodGet, url, nil)
229+
re.NoError(err)
230+
r.ServeHTTP(response, request)
231+
// handled by `cleanPath` which is in `mux.ServeHTTP`
232+
result := response.Result()
233+
defer result.Body.Close()
234+
re.NotNil(result.Header["Location"])
235+
re.Contains(result.Header["Location"][0], "/pd/api/v1/config")
236+
}

server/handler.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"fmt"
2222
"net/http"
2323
"path"
24+
"path/filepath"
2425
"strconv"
2526
"strings"
2627
"time"
@@ -967,6 +968,13 @@ func (h *Handler) PluginLoad(pluginPath string) error {
967968
c := cluster.GetCoordinator()
968969
ch := make(chan string)
969970
h.pluginChMap[pluginPath] = ch
971+
972+
// make sure path is in data dir
973+
filePath, err := filepath.Abs(pluginPath)
974+
if err != nil || !isPathInDirectory(filePath, h.s.GetConfig().DataDir) {
975+
return errs.ErrFilePathAbs.Wrap(err).FastGenWithCause()
976+
}
977+
970978
c.LoadPlugin(pluginPath, ch)
971979
return nil
972980
}

server/replication/replication_mode.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ type FileReplicater interface {
6060
ReplicateFileToMember(ctx context.Context, member *pdpb.Member, name string, data []byte) error
6161
}
6262

63-
const drStatusFile = "DR_STATE"
63+
// DrStatusFile is the file name that stores the dr status.
64+
const DrStatusFile = "DR_STATE"
6465
const persistFileTimeout = time.Second * 3
6566

6667
// ModeManager is used to control how raft logs are synchronized between
@@ -483,7 +484,7 @@ func (m *ModeManager) tickReplicateStatus() {
483484
stateID, ok := m.replicateState.Load(member.GetMemberId())
484485
if !ok || stateID.(uint64) != state.StateID {
485486
ctx, cancel := context.WithTimeout(context.Background(), persistFileTimeout)
486-
err := m.fileReplicater.ReplicateFileToMember(ctx, member, drStatusFile, data)
487+
err := m.fileReplicater.ReplicateFileToMember(ctx, member, DrStatusFile, data)
487488
if err != nil {
488489
log.Warn("failed to switch state", zap.String("replicate-mode", modeDRAutoSync), zap.String("new-state", state.State), errs.ZapError(err))
489490
} else {

server/server.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import (
5959
"github.com/tikv/pd/server/keyspace"
6060
"github.com/tikv/pd/server/member"
6161
syncer "github.com/tikv/pd/server/region_syncer"
62+
"github.com/tikv/pd/server/replication"
6263
"github.com/tikv/pd/server/schedule"
6364
"github.com/tikv/pd/server/schedule/hbstream"
6465
"github.com/tikv/pd/server/schedule/placement"
@@ -1678,8 +1679,15 @@ func (s *Server) ReplicateFileToMember(ctx context.Context, member *pdpb.Member,
16781679

16791680
// PersistFile saves a file in DataDir.
16801681
func (s *Server) PersistFile(name string, data []byte) error {
1682+
if name != replication.DrStatusFile {
1683+
return errors.New("Invalid file name")
1684+
}
16811685
log.Info("persist file", zap.String("name", name), zap.Binary("data", data))
1682-
return os.WriteFile(filepath.Join(s.GetConfig().DataDir, name), data, 0644) // #nosec
1686+
path := filepath.Join(s.GetConfig().DataDir, name)
1687+
if !isPathInDirectory(path, s.GetConfig().DataDir) {
1688+
return errors.New("Invalid file path")
1689+
}
1690+
return os.WriteFile(path, data, 0644) // #nosec
16831691
}
16841692

16851693
// SaveTTLConfig save ttl config

server/server_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"fmt"
2020
"io"
2121
"net/http"
22+
"path/filepath"
2223
"testing"
2324

2425
"github.com/stretchr/testify/suite"
@@ -337,3 +338,14 @@ func (suite *leaderServerTestSuite) TestSourceIpForHeaderBoth() {
337338
bodyString := string(bodyBytes)
338339
suite.Equal("Hello World\n", bodyString)
339340
}
341+
342+
func (suite *leaderServerTestSuite) TestIsPathInDirectory() {
343+
fileName := "test"
344+
directory := "/root/project"
345+
path := filepath.Join(directory, fileName)
346+
suite.True(isPathInDirectory(path, directory))
347+
348+
fileName = "../../test"
349+
path = filepath.Join(directory, fileName)
350+
suite.False(isPathInDirectory(path, directory))
351+
}

0 commit comments

Comments
 (0)