internal_test.go 65 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230
  1. // Copyright (C) 2019 Nicola Murino
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as published
  5. // by the Free Software Foundation, version 3.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU Affero General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU Affero General Public License
  13. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. package sftpd
  15. import (
  16. "bytes"
  17. "errors"
  18. "fmt"
  19. "io"
  20. "io/fs"
  21. "net"
  22. "os"
  23. "path/filepath"
  24. "runtime"
  25. "slices"
  26. "testing"
  27. "time"
  28. "github.com/eikenb/pipeat"
  29. "github.com/pkg/sftp"
  30. "github.com/rs/xid"
  31. "github.com/sftpgo/sdk"
  32. "github.com/stretchr/testify/assert"
  33. "github.com/stretchr/testify/require"
  34. "golang.org/x/crypto/ssh"
  35. "github.com/drakkan/sftpgo/v2/internal/common"
  36. "github.com/drakkan/sftpgo/v2/internal/dataprovider"
  37. "github.com/drakkan/sftpgo/v2/internal/kms"
  38. "github.com/drakkan/sftpgo/v2/internal/util"
  39. "github.com/drakkan/sftpgo/v2/internal/vfs"
  40. )
  41. const (
  42. osWindows = "windows"
  43. )
  44. var (
  45. configDir = filepath.Join(".", "..", "..")
  46. )
  47. type MockChannel struct {
  48. Buffer *bytes.Buffer
  49. StdErrBuffer *bytes.Buffer
  50. ReadError error
  51. WriteError error
  52. ShortWriteErr bool
  53. }
  54. func (c *MockChannel) Read(data []byte) (int, error) {
  55. if c.ReadError != nil {
  56. return 0, c.ReadError
  57. }
  58. return c.Buffer.Read(data)
  59. }
  60. func (c *MockChannel) Write(data []byte) (int, error) {
  61. if c.WriteError != nil {
  62. return 0, c.WriteError
  63. }
  64. if c.ShortWriteErr {
  65. return 0, nil
  66. }
  67. return c.Buffer.Write(data)
  68. }
  69. func (c *MockChannel) Close() error {
  70. return nil
  71. }
  72. func (c *MockChannel) CloseWrite() error {
  73. return nil
  74. }
  75. func (c *MockChannel) SendRequest(_ string, _ bool, _ []byte) (bool, error) {
  76. return true, nil
  77. }
  78. func (c *MockChannel) Stderr() io.ReadWriter {
  79. return c.StdErrBuffer
  80. }
  81. // MockOsFs mockable OsFs
  82. type MockOsFs struct {
  83. vfs.Fs
  84. err error
  85. statErr error
  86. isAtomicUploadSupported bool
  87. }
  88. // Name returns the name for the Fs implementation
  89. func (fs MockOsFs) Name() string {
  90. return "mockOsFs"
  91. }
  92. // IsUploadResumeSupported returns true if resuming uploads is supported
  93. func (MockOsFs) IsUploadResumeSupported() bool {
  94. return false
  95. }
  96. // IsConditionalUploadResumeSupported returns if resuming uploads is supported
  97. // for the specified size
  98. func (MockOsFs) IsConditionalUploadResumeSupported(_ int64) bool {
  99. return false
  100. }
  101. // IsAtomicUploadSupported returns true if atomic upload is supported
  102. func (fs MockOsFs) IsAtomicUploadSupported() bool {
  103. return fs.isAtomicUploadSupported
  104. }
  105. // Stat returns a FileInfo describing the named file
  106. func (fs MockOsFs) Stat(name string) (os.FileInfo, error) {
  107. if fs.statErr != nil {
  108. return nil, fs.statErr
  109. }
  110. return os.Stat(name)
  111. }
  112. // Lstat returns a FileInfo describing the named file
  113. func (fs MockOsFs) Lstat(name string) (os.FileInfo, error) {
  114. if fs.statErr != nil {
  115. return nil, fs.statErr
  116. }
  117. return os.Lstat(name)
  118. }
  119. // Remove removes the named file or (empty) directory.
  120. func (fs MockOsFs) Remove(name string, _ bool) error {
  121. if fs.err != nil {
  122. return fs.err
  123. }
  124. return os.Remove(name)
  125. }
  126. // Rename renames (moves) source to target
  127. func (fs MockOsFs) Rename(source, target string, _ int) (int, int64, error) {
  128. if fs.err != nil {
  129. return -1, -1, fs.err
  130. }
  131. err := os.Rename(source, target)
  132. return -1, -1, err
  133. }
  134. func newMockOsFs(err, statErr error, atomicUpload bool, connectionID, rootDir string) vfs.Fs {
  135. return &MockOsFs{
  136. Fs: vfs.NewOsFs(connectionID, rootDir, "", nil),
  137. err: err,
  138. statErr: statErr,
  139. isAtomicUploadSupported: atomicUpload,
  140. }
  141. }
  142. func TestRemoveNonexistentQuotaScan(t *testing.T) {
  143. assert.False(t, common.QuotaScans.RemoveUserQuotaScan("username"))
  144. }
  145. func TestGetOSOpenFlags(t *testing.T) {
  146. var flags sftp.FileOpenFlags
  147. flags.Write = true
  148. flags.Excl = true
  149. osFlags := getOSOpenFlags(flags)
  150. assert.NotEqual(t, 0, osFlags&os.O_WRONLY)
  151. assert.NotEqual(t, 0, osFlags&os.O_EXCL)
  152. flags.Append = true
  153. // append flag should be ignored to allow resume
  154. assert.NotEqual(t, 0, osFlags&os.O_WRONLY)
  155. assert.NotEqual(t, 0, osFlags&os.O_EXCL)
  156. }
  157. func TestUploadResumeInvalidOffset(t *testing.T) {
  158. testfile := "testfile" //nolint:goconst
  159. file, err := os.Create(testfile)
  160. assert.NoError(t, err)
  161. user := dataprovider.User{
  162. BaseUser: sdk.BaseUser{
  163. Username: "testuser",
  164. },
  165. }
  166. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  167. conn := common.NewBaseConnection("", common.ProtocolSFTP, "", "", user)
  168. baseTransfer := common.NewBaseTransfer(file, conn, nil, file.Name(), file.Name(), testfile,
  169. common.TransferUpload, 10, 0, 0, 0, false, fs, dataprovider.TransferQuota{})
  170. transfer := newTransfer(baseTransfer, nil, nil, nil)
  171. _, err = transfer.WriteAt([]byte("test"), 0)
  172. assert.Error(t, err, "upload with invalid offset must fail")
  173. if assert.Error(t, transfer.ErrTransfer) {
  174. assert.EqualError(t, err, transfer.ErrTransfer.Error())
  175. assert.Contains(t, transfer.ErrTransfer.Error(), "invalid write offset")
  176. }
  177. err = transfer.Close()
  178. if assert.Error(t, err) {
  179. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  180. }
  181. err = os.Remove(testfile)
  182. assert.NoError(t, err)
  183. }
  184. func TestReadWriteErrors(t *testing.T) {
  185. testfile := "testfile"
  186. file, err := os.Create(testfile)
  187. assert.NoError(t, err)
  188. user := dataprovider.User{
  189. BaseUser: sdk.BaseUser{
  190. Username: "testuser",
  191. },
  192. }
  193. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  194. conn := common.NewBaseConnection("", common.ProtocolSFTP, "", "", user)
  195. baseTransfer := common.NewBaseTransfer(file, conn, nil, file.Name(), file.Name(), testfile, common.TransferDownload,
  196. 0, 0, 0, 0, false, fs, dataprovider.TransferQuota{})
  197. transfer := newTransfer(baseTransfer, nil, nil, nil)
  198. err = file.Close()
  199. assert.NoError(t, err)
  200. _, err = transfer.WriteAt([]byte("test"), 0)
  201. assert.Error(t, err, "writing to closed file must fail")
  202. buf := make([]byte, 32768)
  203. _, err = transfer.ReadAt(buf, 0)
  204. assert.Error(t, err, "reading from a closed file must fail")
  205. err = transfer.Close()
  206. assert.Error(t, err)
  207. r, _, err := pipeat.Pipe()
  208. assert.NoError(t, err)
  209. baseTransfer = common.NewBaseTransfer(nil, conn, nil, file.Name(), file.Name(), testfile, common.TransferDownload,
  210. 0, 0, 0, 0, false, fs, dataprovider.TransferQuota{})
  211. transfer = newTransfer(baseTransfer, nil, vfs.NewPipeReader(r), nil)
  212. err = transfer.Close()
  213. assert.NoError(t, err)
  214. _, err = transfer.ReadAt(buf, 0)
  215. assert.Error(t, err, "reading from a closed pipe must fail")
  216. r, w, err := pipeat.Pipe()
  217. assert.NoError(t, err)
  218. pipeWriter := vfs.NewPipeWriter(w)
  219. baseTransfer = common.NewBaseTransfer(nil, conn, nil, file.Name(), file.Name(), testfile, common.TransferDownload,
  220. 0, 0, 0, 0, false, fs, dataprovider.TransferQuota{})
  221. transfer = newTransfer(baseTransfer, pipeWriter, nil, nil)
  222. err = r.Close()
  223. assert.NoError(t, err)
  224. errFake := fmt.Errorf("fake upload error")
  225. go func() {
  226. time.Sleep(100 * time.Millisecond)
  227. pipeWriter.Done(errFake)
  228. }()
  229. err = transfer.closeIO()
  230. assert.EqualError(t, err, errFake.Error())
  231. _, err = transfer.WriteAt([]byte("test"), 0)
  232. assert.Error(t, err, "writing to closed pipe must fail")
  233. err = transfer.BaseTransfer.Close()
  234. assert.EqualError(t, err, errFake.Error())
  235. err = os.Remove(testfile)
  236. assert.NoError(t, err)
  237. assert.Len(t, conn.GetTransfers(), 0)
  238. }
  239. func TestUnsupportedListOP(t *testing.T) {
  240. conn := common.NewBaseConnection("", common.ProtocolSFTP, "", "", dataprovider.User{})
  241. sftpConn := Connection{
  242. BaseConnection: conn,
  243. }
  244. request := sftp.NewRequest("Unsupported", "")
  245. _, err := sftpConn.Filelist(request)
  246. assert.EqualError(t, err, sftp.ErrSSHFxOpUnsupported.Error())
  247. }
  248. func TestTransferCancelFn(t *testing.T) {
  249. testfile := "testfile"
  250. file, err := os.Create(testfile)
  251. assert.NoError(t, err)
  252. isCancelled := false
  253. cancelFn := func() {
  254. isCancelled = true
  255. }
  256. user := dataprovider.User{
  257. BaseUser: sdk.BaseUser{
  258. Username: "testuser",
  259. },
  260. }
  261. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  262. conn := common.NewBaseConnection("", common.ProtocolSFTP, "", "", user)
  263. baseTransfer := common.NewBaseTransfer(file, conn, cancelFn, file.Name(), file.Name(), testfile, common.TransferDownload,
  264. 0, 0, 0, 0, false, fs, dataprovider.TransferQuota{})
  265. transfer := newTransfer(baseTransfer, nil, nil, nil)
  266. errFake := errors.New("fake error, this will trigger cancelFn")
  267. transfer.TransferError(errFake)
  268. err = transfer.Close()
  269. if assert.Error(t, err) {
  270. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  271. }
  272. if assert.Error(t, transfer.ErrTransfer) {
  273. assert.EqualError(t, transfer.ErrTransfer, errFake.Error())
  274. }
  275. assert.True(t, isCancelled, "cancelFn not called!")
  276. err = os.Remove(testfile)
  277. assert.NoError(t, err)
  278. }
  279. func TestUploadFiles(t *testing.T) {
  280. common.Config.UploadMode = common.UploadModeAtomic
  281. fs := vfs.NewOsFs("123", os.TempDir(), "", nil)
  282. u := dataprovider.User{}
  283. c := Connection{
  284. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", u),
  285. }
  286. var flags sftp.FileOpenFlags
  287. flags.Write = true
  288. flags.Trunc = true
  289. _, err := c.handleSFTPUploadToExistingFile(fs, flags, "missing_path", "other_missing_path", 0, "/missing_path", nil)
  290. assert.Error(t, err, "upload to existing file must fail if one or both paths are invalid")
  291. common.Config.UploadMode = common.UploadModeStandard
  292. _, err = c.handleSFTPUploadToExistingFile(fs, flags, "missing_path", "other_missing_path", 0, "/missing_path", nil)
  293. assert.Error(t, err, "upload to existing file must fail if one or both paths are invalid")
  294. missingFile := "missing/relative/file.txt"
  295. if runtime.GOOS == osWindows {
  296. missingFile = "missing\\relative\\file.txt"
  297. }
  298. _, err = c.handleSFTPUploadToNewFile(fs, flags, ".", missingFile, "/missing", nil)
  299. assert.Error(t, err, "upload new file in missing path must fail")
  300. fs = newMockOsFs(nil, nil, false, "123", os.TempDir())
  301. f, err := os.CreateTemp("", "temp")
  302. assert.NoError(t, err)
  303. err = f.Close()
  304. assert.NoError(t, err)
  305. tr, err := c.handleSFTPUploadToExistingFile(fs, flags, f.Name(), f.Name(), 123, f.Name(), nil)
  306. if assert.NoError(t, err) {
  307. transfer := tr.(*transfer)
  308. transfers := c.GetTransfers()
  309. if assert.Equal(t, 1, len(transfers)) {
  310. assert.Equal(t, transfers[0].ID, transfer.GetID())
  311. assert.Equal(t, int64(123), transfer.InitialSize)
  312. err = transfer.Close()
  313. assert.NoError(t, err)
  314. assert.Equal(t, 0, len(c.GetTransfers()))
  315. }
  316. }
  317. err = os.Remove(f.Name())
  318. assert.NoError(t, err)
  319. common.Config.UploadMode = common.UploadModeAtomicWithResume
  320. }
  321. func TestWithInvalidHome(t *testing.T) {
  322. u := dataprovider.User{}
  323. u.HomeDir = "home_rel_path" //nolint:goconst
  324. _, err := loginUser(&u, dataprovider.LoginMethodPassword, "", nil)
  325. assert.Error(t, err, "login a user with an invalid home_dir must fail")
  326. u.HomeDir = os.TempDir()
  327. fs, err := u.GetFilesystem("123")
  328. assert.NoError(t, err)
  329. c := Connection{
  330. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", u),
  331. }
  332. _, err = fs.ResolvePath("../upper_path")
  333. assert.Error(t, err, "tested path is not a home subdir")
  334. _, err = c.StatVFS(&sftp.Request{
  335. Method: "StatVFS",
  336. Filepath: "../unresolvable-path",
  337. })
  338. assert.Error(t, err)
  339. }
  340. func TestResolveWithRootDir(t *testing.T) {
  341. u := dataprovider.User{}
  342. if runtime.GOOS == osWindows {
  343. u.HomeDir = "C:\\"
  344. } else {
  345. u.HomeDir = "/"
  346. }
  347. fs, err := u.GetFilesystem("")
  348. assert.NoError(t, err)
  349. rel, err := filepath.Rel(u.HomeDir, os.TempDir())
  350. assert.NoError(t, err)
  351. p, err := fs.ResolvePath(rel)
  352. assert.NoError(t, err, "path %v", p)
  353. }
  354. func TestSFTPGetUsedQuota(t *testing.T) {
  355. u := dataprovider.User{}
  356. u.HomeDir = "home_rel_path"
  357. u.Username = "test_invalid_user"
  358. u.QuotaSize = 4096
  359. u.QuotaFiles = 1
  360. u.Permissions = make(map[string][]string)
  361. u.Permissions["/"] = []string{dataprovider.PermAny}
  362. connection := Connection{
  363. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", u),
  364. }
  365. quotaResult, _ := connection.HasSpace(false, false, "/")
  366. assert.False(t, quotaResult.HasSpace)
  367. }
  368. func TestSupportedSSHCommands(t *testing.T) {
  369. cmds := GetSupportedSSHCommands()
  370. assert.Equal(t, len(supportedSSHCommands), len(cmds))
  371. for _, c := range cmds {
  372. assert.True(t, slices.Contains(supportedSSHCommands, c))
  373. }
  374. }
  375. func TestSSHCommandPath(t *testing.T) {
  376. buf := make([]byte, 65535)
  377. stdErrBuf := make([]byte, 65535)
  378. mockSSHChannel := MockChannel{
  379. Buffer: bytes.NewBuffer(buf),
  380. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  381. ReadError: nil,
  382. }
  383. connection := &Connection{
  384. channel: &mockSSHChannel,
  385. BaseConnection: common.NewBaseConnection("", common.ProtocolSSH, "", "", dataprovider.User{}),
  386. }
  387. sshCommand := sshCommand{
  388. command: "test",
  389. connection: connection,
  390. args: []string{},
  391. }
  392. assert.Equal(t, "", sshCommand.getDestPath())
  393. sshCommand.args = []string{"-t", "/tmp/../path"}
  394. assert.Equal(t, "/path", sshCommand.getDestPath())
  395. sshCommand.args = []string{"-t", "/tmp/"}
  396. assert.Equal(t, "/tmp/", sshCommand.getDestPath())
  397. sshCommand.args = []string{"-t", "tmp/"}
  398. assert.Equal(t, "/tmp/", sshCommand.getDestPath())
  399. sshCommand.args = []string{"-t", "/tmp/../../../path"}
  400. assert.Equal(t, "/path", sshCommand.getDestPath())
  401. sshCommand.args = []string{"-t", ".."}
  402. assert.Equal(t, "/", sshCommand.getDestPath())
  403. sshCommand.args = []string{"-t", "."}
  404. assert.Equal(t, "/", sshCommand.getDestPath())
  405. sshCommand.args = []string{"-t", "//"}
  406. assert.Equal(t, "/", sshCommand.getDestPath())
  407. sshCommand.args = []string{"-t", "../.."}
  408. assert.Equal(t, "/", sshCommand.getDestPath())
  409. sshCommand.args = []string{"-t", "/.."}
  410. assert.Equal(t, "/", sshCommand.getDestPath())
  411. sshCommand.args = []string{"-f", "/a space.txt"}
  412. assert.Equal(t, "/a space.txt", sshCommand.getDestPath())
  413. }
  414. func TestSSHParseCommandPayload(t *testing.T) {
  415. cmd := "command -a -f /ab\\ à/some\\ spaces\\ \\ \\(\\).txt"
  416. name, args, _ := parseCommandPayload(cmd)
  417. assert.Equal(t, "command", name)
  418. assert.Equal(t, 3, len(args))
  419. assert.Equal(t, "/ab à/some spaces ().txt", args[2])
  420. _, _, err := parseCommandPayload("")
  421. assert.Error(t, err, "parsing invalid command must fail")
  422. }
  423. func TestSSHCommandErrors(t *testing.T) {
  424. buf := make([]byte, 65535)
  425. stdErrBuf := make([]byte, 65535)
  426. readErr := fmt.Errorf("test read error")
  427. mockSSHChannel := MockChannel{
  428. Buffer: bytes.NewBuffer(buf),
  429. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  430. ReadError: readErr,
  431. }
  432. server, client := net.Pipe()
  433. defer func() {
  434. err := server.Close()
  435. assert.NoError(t, err)
  436. }()
  437. defer func() {
  438. err := client.Close()
  439. assert.NoError(t, err)
  440. }()
  441. user := dataprovider.User{}
  442. user.Permissions = map[string][]string{
  443. "/": {dataprovider.PermAny},
  444. }
  445. connection := Connection{
  446. BaseConnection: common.NewBaseConnection("", common.ProtocolSSH, "", "", user),
  447. channel: &mockSSHChannel,
  448. }
  449. cmd := sshCommand{
  450. command: "md5sum",
  451. connection: &connection,
  452. args: []string{},
  453. }
  454. err := cmd.handle()
  455. assert.Error(t, err, "ssh command must fail, we are sending a fake error")
  456. cmd = sshCommand{
  457. command: "md5sum",
  458. connection: &connection,
  459. args: []string{"/../../test_file_ftp.dat"},
  460. }
  461. err = cmd.handle()
  462. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  463. cmd = sshCommand{
  464. command: "git-receive-pack",
  465. connection: &connection,
  466. args: []string{"/../../testrepo"},
  467. }
  468. err = cmd.handle()
  469. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  470. user = dataprovider.User{}
  471. user.Permissions = map[string][]string{
  472. "/": {dataprovider.PermAny},
  473. }
  474. user.HomeDir = filepath.Clean(os.TempDir())
  475. user.QuotaFiles = 1
  476. user.UsedQuotaFiles = 2
  477. cmd.connection.User = user
  478. _, err = cmd.connection.User.GetFilesystem("123")
  479. assert.NoError(t, err)
  480. err = cmd.handle()
  481. assert.EqualError(t, err, common.ErrQuotaExceeded.Error())
  482. cmd.connection.User.QuotaFiles = 0
  483. cmd.connection.User.UsedQuotaFiles = 0
  484. cmd.connection.User.Permissions = make(map[string][]string)
  485. cmd.connection.User.Permissions["/"] = []string{dataprovider.PermListItems}
  486. err = cmd.handle()
  487. assert.EqualError(t, err, common.ErrPermissionDenied.Error())
  488. cmd.connection.User.Permissions["/"] = []string{dataprovider.PermAny}
  489. cmd.command = "invalid_command"
  490. command, err := cmd.getSystemCommand()
  491. assert.NoError(t, err)
  492. err = cmd.executeSystemCommand(command)
  493. assert.Error(t, err, "invalid command must fail")
  494. command, err = cmd.getSystemCommand()
  495. assert.NoError(t, err)
  496. _, err = command.cmd.StderrPipe()
  497. assert.NoError(t, err)
  498. err = cmd.executeSystemCommand(command)
  499. assert.Error(t, err, "command must fail, pipe was already assigned")
  500. err = cmd.executeSystemCommand(command)
  501. assert.Error(t, err, "command must fail, pipe was already assigned")
  502. command, err = cmd.getSystemCommand()
  503. assert.NoError(t, err)
  504. _, err = command.cmd.StdoutPipe()
  505. assert.NoError(t, err)
  506. err = cmd.executeSystemCommand(command)
  507. assert.Error(t, err, "command must fail, pipe was already assigned")
  508. cmd = sshCommand{
  509. command: "sftpgo-remove",
  510. connection: &connection,
  511. args: []string{"/../../src"},
  512. }
  513. err = cmd.handle()
  514. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  515. cmd = sshCommand{
  516. command: "sftpgo-copy",
  517. connection: &connection,
  518. args: []string{"/../../test_src", "."},
  519. }
  520. err = cmd.handle()
  521. assert.Error(t, err, "ssh command must fail, we are requesting an invalid path")
  522. cmd.connection.User.HomeDir = filepath.Clean(os.TempDir())
  523. cmd = sshCommand{
  524. command: "sftpgo-copy",
  525. connection: &connection,
  526. args: []string{"src", "dst"},
  527. }
  528. cmd.connection.User.Permissions = make(map[string][]string)
  529. cmd.connection.User.Permissions["/"] = []string{dataprovider.PermAny}
  530. common.WaitForTransfers(1)
  531. _, err = cmd.getSystemCommand()
  532. if assert.Error(t, err) {
  533. assert.Contains(t, err.Error(), common.ErrShuttingDown.Error())
  534. }
  535. err = common.Initialize(common.Config, 0)
  536. assert.NoError(t, err)
  537. }
  538. func TestCommandsWithExtensionsFilter(t *testing.T) {
  539. buf := make([]byte, 65535)
  540. stdErrBuf := make([]byte, 65535)
  541. mockSSHChannel := MockChannel{
  542. Buffer: bytes.NewBuffer(buf),
  543. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  544. }
  545. server, client := net.Pipe()
  546. defer server.Close()
  547. defer client.Close()
  548. user := dataprovider.User{
  549. BaseUser: sdk.BaseUser{
  550. Username: "test",
  551. HomeDir: os.TempDir(),
  552. Status: 1,
  553. },
  554. }
  555. user.Filters.FilePatterns = []sdk.PatternsFilter{
  556. {
  557. Path: "/subdir",
  558. AllowedPatterns: []string{".jpg"},
  559. DeniedPatterns: []string{},
  560. },
  561. }
  562. connection := &Connection{
  563. BaseConnection: common.NewBaseConnection("", common.ProtocolSSH, "", "", user),
  564. channel: &mockSSHChannel,
  565. }
  566. cmd := sshCommand{
  567. command: "md5sum",
  568. connection: connection,
  569. args: []string{"subdir/test.png"},
  570. }
  571. err := cmd.handleHashCommands()
  572. assert.EqualError(t, err, common.ErrPermissionDenied.Error())
  573. cmd = sshCommand{
  574. command: "rsync",
  575. connection: connection,
  576. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  577. }
  578. _, err = cmd.getSystemCommand()
  579. assert.EqualError(t, err, errUnsupportedConfig.Error())
  580. cmd = sshCommand{
  581. command: "git-receive-pack",
  582. connection: connection,
  583. args: []string{"/subdir"},
  584. }
  585. _, err = cmd.getSystemCommand()
  586. assert.EqualError(t, err, errUnsupportedConfig.Error())
  587. cmd = sshCommand{
  588. command: "git-receive-pack",
  589. connection: connection,
  590. args: []string{"/subdir/dir"},
  591. }
  592. _, err = cmd.getSystemCommand()
  593. assert.EqualError(t, err, errUnsupportedConfig.Error())
  594. cmd = sshCommand{
  595. command: "git-receive-pack",
  596. connection: connection,
  597. args: []string{"/adir/subdir"},
  598. }
  599. _, err = cmd.getSystemCommand()
  600. assert.NoError(t, err)
  601. }
  602. func TestSSHCommandsRemoteFs(t *testing.T) {
  603. buf := make([]byte, 65535)
  604. stdErrBuf := make([]byte, 65535)
  605. mockSSHChannel := MockChannel{
  606. Buffer: bytes.NewBuffer(buf),
  607. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  608. }
  609. user := dataprovider.User{}
  610. user.FsConfig = vfs.Filesystem{
  611. Provider: sdk.S3FilesystemProvider,
  612. S3Config: vfs.S3FsConfig{
  613. BaseS3FsConfig: sdk.BaseS3FsConfig{
  614. Bucket: "s3bucket",
  615. Endpoint: "endpoint",
  616. Region: "eu-west-1",
  617. },
  618. },
  619. }
  620. connection := &Connection{
  621. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  622. channel: &mockSSHChannel,
  623. }
  624. cmd := sshCommand{
  625. command: "md5sum",
  626. connection: connection,
  627. args: []string{},
  628. }
  629. command, err := cmd.getSystemCommand()
  630. assert.NoError(t, err)
  631. err = cmd.executeSystemCommand(command)
  632. assert.Error(t, err, "command must fail for a non local filesystem")
  633. cmd = sshCommand{
  634. command: "sftpgo-copy",
  635. connection: connection,
  636. args: []string{},
  637. }
  638. err = cmd.handleSFTPGoCopy()
  639. assert.Error(t, err)
  640. cmd = sshCommand{
  641. command: "sftpgo-remove",
  642. connection: connection,
  643. args: []string{},
  644. }
  645. err = cmd.handleSFTPGoRemove()
  646. assert.Error(t, err)
  647. }
  648. func TestSSHCmdGetFsErrors(t *testing.T) {
  649. buf := make([]byte, 65535)
  650. stdErrBuf := make([]byte, 65535)
  651. mockSSHChannel := MockChannel{
  652. Buffer: bytes.NewBuffer(buf),
  653. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  654. }
  655. user := dataprovider.User{
  656. BaseUser: sdk.BaseUser{
  657. HomeDir: "relative path",
  658. },
  659. }
  660. user.Permissions = map[string][]string{}
  661. user.Permissions["/"] = []string{dataprovider.PermAny}
  662. connection := &Connection{
  663. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  664. channel: &mockSSHChannel,
  665. }
  666. cmd := sshCommand{
  667. command: "sftpgo-remove",
  668. connection: connection,
  669. args: []string{"path"},
  670. }
  671. err := cmd.handleSFTPGoRemove()
  672. assert.Error(t, err)
  673. cmd = sshCommand{
  674. command: "sftpgo-copy",
  675. connection: connection,
  676. args: []string{"path1", "path2"},
  677. }
  678. err = cmd.handleSFTPGoCopy()
  679. assert.Error(t, err)
  680. err = os.RemoveAll(user.GetHomeDir())
  681. assert.NoError(t, err)
  682. }
  683. func TestGitVirtualFolders(t *testing.T) {
  684. permissions := make(map[string][]string)
  685. permissions["/"] = []string{dataprovider.PermAny}
  686. user := dataprovider.User{
  687. BaseUser: sdk.BaseUser{
  688. Permissions: permissions,
  689. HomeDir: os.TempDir(),
  690. },
  691. }
  692. conn := &Connection{
  693. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  694. }
  695. cmd := sshCommand{
  696. command: "git-receive-pack",
  697. connection: conn,
  698. args: []string{"/vdir"},
  699. }
  700. cmd.connection.User.VirtualFolders = append(cmd.connection.User.VirtualFolders, vfs.VirtualFolder{
  701. BaseVirtualFolder: vfs.BaseVirtualFolder{
  702. MappedPath: os.TempDir(),
  703. },
  704. VirtualPath: "/vdir",
  705. })
  706. _, err := cmd.getSystemCommand()
  707. assert.NoError(t, err)
  708. cmd.args = []string{"/"}
  709. _, err = cmd.getSystemCommand()
  710. assert.EqualError(t, err, errUnsupportedConfig.Error())
  711. cmd.args = []string{"/vdir1"}
  712. _, err = cmd.getSystemCommand()
  713. assert.NoError(t, err)
  714. cmd.connection.User.VirtualFolders = nil
  715. cmd.connection.User.VirtualFolders = append(cmd.connection.User.VirtualFolders, vfs.VirtualFolder{
  716. BaseVirtualFolder: vfs.BaseVirtualFolder{
  717. MappedPath: os.TempDir(),
  718. },
  719. VirtualPath: "/vdir",
  720. })
  721. cmd.args = []string{"/vdir/subdir"}
  722. _, err = cmd.getSystemCommand()
  723. assert.NoError(t, err)
  724. cmd.args = []string{"/adir/subdir"}
  725. _, err = cmd.getSystemCommand()
  726. assert.NoError(t, err)
  727. }
  728. func TestRsyncOptions(t *testing.T) {
  729. permissions := make(map[string][]string)
  730. permissions["/"] = []string{dataprovider.PermAny}
  731. user := dataprovider.User{
  732. BaseUser: sdk.BaseUser{
  733. Permissions: permissions,
  734. HomeDir: os.TempDir(),
  735. },
  736. }
  737. conn := &Connection{
  738. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  739. }
  740. sshCmd := sshCommand{
  741. command: "rsync",
  742. connection: conn,
  743. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  744. }
  745. cmd, err := sshCmd.getSystemCommand()
  746. assert.NoError(t, err)
  747. assert.True(t, slices.Contains(cmd.cmd.Args, "--safe-links"),
  748. "--safe-links must be added if the user has the create symlinks permission")
  749. permissions["/"] = []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermCreateDirs,
  750. dataprovider.PermListItems, dataprovider.PermOverwrite, dataprovider.PermDelete, dataprovider.PermRename}
  751. user.Permissions = permissions
  752. conn = &Connection{
  753. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  754. }
  755. sshCmd = sshCommand{
  756. command: "rsync",
  757. connection: conn,
  758. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  759. }
  760. cmd, err = sshCmd.getSystemCommand()
  761. assert.NoError(t, err)
  762. assert.True(t, slices.Contains(cmd.cmd.Args, "--munge-links"),
  763. "--munge-links must be added if the user has the create symlinks permission")
  764. sshCmd.connection.User.VirtualFolders = append(sshCmd.connection.User.VirtualFolders, vfs.VirtualFolder{
  765. BaseVirtualFolder: vfs.BaseVirtualFolder{
  766. MappedPath: os.TempDir(),
  767. },
  768. VirtualPath: "/vdir",
  769. })
  770. _, err = sshCmd.getSystemCommand()
  771. assert.EqualError(t, err, errUnsupportedConfig.Error())
  772. }
  773. func TestSystemCommandSizeForPath(t *testing.T) {
  774. permissions := make(map[string][]string)
  775. permissions["/"] = []string{dataprovider.PermAny}
  776. user := dataprovider.User{
  777. BaseUser: sdk.BaseUser{
  778. Permissions: permissions,
  779. HomeDir: os.TempDir(),
  780. },
  781. }
  782. fs, err := user.GetFilesystem("123")
  783. assert.NoError(t, err)
  784. conn := &Connection{
  785. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  786. }
  787. sshCmd := sshCommand{
  788. command: "rsync",
  789. connection: conn,
  790. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  791. }
  792. _, _, err = sshCmd.getSizeForPath(fs, "missing path")
  793. assert.NoError(t, err)
  794. testDir := filepath.Join(os.TempDir(), "dir")
  795. err = os.MkdirAll(testDir, os.ModePerm)
  796. assert.NoError(t, err)
  797. testFile := filepath.Join(testDir, "testfile")
  798. err = os.WriteFile(testFile, []byte("test content"), os.ModePerm)
  799. assert.NoError(t, err)
  800. err = os.Symlink(testFile, testFile+".link")
  801. assert.NoError(t, err)
  802. numFiles, size, err := sshCmd.getSizeForPath(fs, testFile+".link")
  803. assert.NoError(t, err)
  804. assert.Equal(t, 0, numFiles)
  805. assert.Equal(t, int64(0), size)
  806. numFiles, size, err = sshCmd.getSizeForPath(fs, testFile)
  807. assert.NoError(t, err)
  808. assert.Equal(t, 1, numFiles)
  809. assert.Equal(t, int64(12), size)
  810. if runtime.GOOS != osWindows {
  811. err = os.Chmod(testDir, 0001)
  812. assert.NoError(t, err)
  813. _, _, err = sshCmd.getSizeForPath(fs, testFile)
  814. assert.Error(t, err)
  815. err = os.Chmod(testDir, os.ModePerm)
  816. assert.NoError(t, err)
  817. }
  818. err = os.RemoveAll(testDir)
  819. assert.NoError(t, err)
  820. }
  821. func TestSystemCommandErrors(t *testing.T) {
  822. buf := make([]byte, 65535)
  823. stdErrBuf := make([]byte, 65535)
  824. readErr := fmt.Errorf("test read error")
  825. writeErr := fmt.Errorf("test write error")
  826. mockSSHChannel := MockChannel{
  827. Buffer: bytes.NewBuffer(buf),
  828. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  829. ReadError: nil,
  830. WriteError: writeErr,
  831. }
  832. permissions := make(map[string][]string)
  833. permissions["/"] = []string{dataprovider.PermAny}
  834. homeDir := filepath.Join(os.TempDir(), "adir")
  835. err := os.MkdirAll(homeDir, os.ModePerm)
  836. assert.NoError(t, err)
  837. err = os.WriteFile(filepath.Join(homeDir, "afile"), []byte("content"), os.ModePerm)
  838. assert.NoError(t, err)
  839. user := dataprovider.User{
  840. BaseUser: sdk.BaseUser{
  841. Permissions: permissions,
  842. HomeDir: homeDir,
  843. },
  844. }
  845. fs, err := user.GetFilesystem("123")
  846. assert.NoError(t, err)
  847. connection := &Connection{
  848. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  849. channel: &mockSSHChannel,
  850. }
  851. var sshCmd sshCommand
  852. if runtime.GOOS == osWindows {
  853. sshCmd = sshCommand{
  854. command: "dir",
  855. connection: connection,
  856. args: []string{"/"},
  857. }
  858. } else {
  859. sshCmd = sshCommand{
  860. command: "ls",
  861. connection: connection,
  862. args: []string{"/"},
  863. }
  864. }
  865. systemCmd, err := sshCmd.getSystemCommand()
  866. assert.NoError(t, err)
  867. systemCmd.cmd.Dir = os.TempDir()
  868. // FIXME: the command completes but the fake client is unable to read the response
  869. // no error is reported in this case. We can see that the expected code is executed
  870. // reading the test coverage
  871. sshCmd.executeSystemCommand(systemCmd) //nolint:errcheck
  872. mockSSHChannel = MockChannel{
  873. Buffer: bytes.NewBuffer(buf),
  874. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  875. ReadError: readErr,
  876. WriteError: nil,
  877. }
  878. sshCmd.connection.channel = &mockSSHChannel
  879. baseTransfer := common.NewBaseTransfer(nil, sshCmd.connection.BaseConnection, nil, "", "", "",
  880. common.TransferUpload, 0, 0, 0, 0, false, fs, dataprovider.TransferQuota{})
  881. transfer := newTransfer(baseTransfer, nil, nil, nil)
  882. destBuff := make([]byte, 65535)
  883. dst := bytes.NewBuffer(destBuff)
  884. _, err = transfer.copyFromReaderToWriter(dst, sshCmd.connection.channel)
  885. assert.EqualError(t, err, readErr.Error())
  886. mockSSHChannel = MockChannel{
  887. Buffer: bytes.NewBuffer(buf),
  888. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  889. ReadError: nil,
  890. WriteError: nil,
  891. }
  892. sshCmd.connection.channel = &mockSSHChannel
  893. transfer.MaxWriteSize = 1
  894. _, err = transfer.copyFromReaderToWriter(dst, sshCmd.connection.channel)
  895. assert.True(t, transfer.Connection.IsQuotaExceededError(err))
  896. mockSSHChannel = MockChannel{
  897. Buffer: bytes.NewBuffer(buf),
  898. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  899. ReadError: nil,
  900. WriteError: nil,
  901. ShortWriteErr: true,
  902. }
  903. sshCmd.connection.channel = &mockSSHChannel
  904. _, err = transfer.copyFromReaderToWriter(sshCmd.connection.channel, dst)
  905. assert.EqualError(t, err, io.ErrShortWrite.Error())
  906. transfer.MaxWriteSize = -1
  907. _, err = transfer.copyFromReaderToWriter(sshCmd.connection.channel, dst)
  908. assert.True(t, transfer.Connection.IsQuotaExceededError(err))
  909. baseTransfer = common.NewBaseTransfer(nil, sshCmd.connection.BaseConnection, nil, "", "", "",
  910. common.TransferDownload, 0, 0, 0, 0, false, fs, dataprovider.TransferQuota{
  911. AllowedDLSize: 1,
  912. })
  913. transfer = newTransfer(baseTransfer, nil, nil, nil)
  914. mockSSHChannel = MockChannel{
  915. Buffer: bytes.NewBuffer(buf),
  916. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  917. ReadError: nil,
  918. WriteError: nil,
  919. }
  920. sshCmd.connection.channel = &mockSSHChannel
  921. _, err = transfer.copyFromReaderToWriter(dst, sshCmd.connection.channel)
  922. if assert.Error(t, err) {
  923. assert.Contains(t, err.Error(), common.ErrReadQuotaExceeded.Error())
  924. }
  925. err = os.RemoveAll(homeDir)
  926. assert.NoError(t, err)
  927. }
  928. func TestCommandGetFsError(t *testing.T) {
  929. user := dataprovider.User{
  930. FsConfig: vfs.Filesystem{
  931. Provider: sdk.CryptedFilesystemProvider,
  932. },
  933. }
  934. conn := &Connection{
  935. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  936. }
  937. sshCmd := sshCommand{
  938. command: "rsync",
  939. connection: conn,
  940. args: []string{"--server", "-vlogDtprze.iLsfxC", ".", "/"},
  941. }
  942. _, err := sshCmd.getSystemCommand()
  943. assert.Error(t, err)
  944. buf := make([]byte, 65535)
  945. stdErrBuf := make([]byte, 65535)
  946. mockSSHChannel := MockChannel{
  947. Buffer: bytes.NewBuffer(buf),
  948. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  949. ReadError: nil,
  950. }
  951. conn = &Connection{
  952. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", user),
  953. channel: &mockSSHChannel,
  954. }
  955. scpCommand := scpCommand{
  956. sshCommand: sshCommand{
  957. command: "scp",
  958. connection: conn,
  959. args: []string{"-t", "/tmp"},
  960. },
  961. }
  962. err = scpCommand.handleRecursiveUpload()
  963. assert.Error(t, err)
  964. err = scpCommand.handleDownload("")
  965. assert.Error(t, err)
  966. }
  967. func TestSCPFileMode(t *testing.T) {
  968. mode := getFileModeAsString(0, true)
  969. assert.Equal(t, "0755", mode)
  970. mode = getFileModeAsString(0700, true)
  971. assert.Equal(t, "0700", mode)
  972. mode = getFileModeAsString(0750, true)
  973. assert.Equal(t, "0750", mode)
  974. mode = getFileModeAsString(0777, true)
  975. assert.Equal(t, "0777", mode)
  976. mode = getFileModeAsString(0640, false)
  977. assert.Equal(t, "0640", mode)
  978. mode = getFileModeAsString(0600, false)
  979. assert.Equal(t, "0600", mode)
  980. mode = getFileModeAsString(0, false)
  981. assert.Equal(t, "0644", mode)
  982. fileMode := uint32(0777)
  983. fileMode = fileMode | uint32(os.ModeSetgid)
  984. fileMode = fileMode | uint32(os.ModeSetuid)
  985. fileMode = fileMode | uint32(os.ModeSticky)
  986. mode = getFileModeAsString(os.FileMode(fileMode), false)
  987. assert.Equal(t, "7777", mode)
  988. fileMode = uint32(0644)
  989. fileMode = fileMode | uint32(os.ModeSetgid)
  990. mode = getFileModeAsString(os.FileMode(fileMode), false)
  991. assert.Equal(t, "4644", mode)
  992. fileMode = uint32(0600)
  993. fileMode = fileMode | uint32(os.ModeSetuid)
  994. mode = getFileModeAsString(os.FileMode(fileMode), false)
  995. assert.Equal(t, "2600", mode)
  996. fileMode = uint32(0044)
  997. fileMode = fileMode | uint32(os.ModeSticky)
  998. mode = getFileModeAsString(os.FileMode(fileMode), false)
  999. assert.Equal(t, "1044", mode)
  1000. }
  1001. func TestSCPUploadError(t *testing.T) {
  1002. buf := make([]byte, 65535)
  1003. stdErrBuf := make([]byte, 65535)
  1004. writeErr := fmt.Errorf("test write error")
  1005. mockSSHChannel := MockChannel{
  1006. Buffer: bytes.NewBuffer(buf),
  1007. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1008. ReadError: nil,
  1009. WriteError: writeErr,
  1010. }
  1011. user := dataprovider.User{
  1012. BaseUser: sdk.BaseUser{
  1013. HomeDir: filepath.Join(os.TempDir()),
  1014. Permissions: make(map[string][]string),
  1015. },
  1016. }
  1017. user.Permissions["/"] = []string{dataprovider.PermAny}
  1018. connection := &Connection{
  1019. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  1020. channel: &mockSSHChannel,
  1021. }
  1022. scpCommand := scpCommand{
  1023. sshCommand: sshCommand{
  1024. command: "scp",
  1025. connection: connection,
  1026. args: []string{"-t", "/"},
  1027. },
  1028. }
  1029. err := scpCommand.handle()
  1030. assert.EqualError(t, err, writeErr.Error())
  1031. mockSSHChannel = MockChannel{
  1032. Buffer: bytes.NewBuffer([]byte("D0755 0 testdir\n")),
  1033. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1034. ReadError: nil,
  1035. WriteError: writeErr,
  1036. }
  1037. err = scpCommand.handleRecursiveUpload()
  1038. assert.EqualError(t, err, writeErr.Error())
  1039. mockSSHChannel = MockChannel{
  1040. Buffer: bytes.NewBuffer([]byte("D0755 a testdir\n")),
  1041. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1042. ReadError: nil,
  1043. WriteError: nil,
  1044. }
  1045. err = scpCommand.handleRecursiveUpload()
  1046. assert.Error(t, err)
  1047. }
  1048. func TestSCPInvalidEndDir(t *testing.T) {
  1049. stdErrBuf := make([]byte, 65535)
  1050. mockSSHChannel := MockChannel{
  1051. Buffer: bytes.NewBuffer([]byte("E\n")),
  1052. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1053. }
  1054. connection := &Connection{
  1055. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", dataprovider.User{
  1056. BaseUser: sdk.BaseUser{
  1057. HomeDir: os.TempDir(),
  1058. },
  1059. }),
  1060. channel: &mockSSHChannel,
  1061. }
  1062. scpCommand := scpCommand{
  1063. sshCommand: sshCommand{
  1064. command: "scp",
  1065. connection: connection,
  1066. args: []string{"-t", "/tmp"},
  1067. },
  1068. }
  1069. err := scpCommand.handleRecursiveUpload()
  1070. assert.EqualError(t, err, "unacceptable end dir command")
  1071. }
  1072. func TestSCPParseUploadMessage(t *testing.T) {
  1073. buf := make([]byte, 65535)
  1074. stdErrBuf := make([]byte, 65535)
  1075. mockSSHChannel := MockChannel{
  1076. Buffer: bytes.NewBuffer(buf),
  1077. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1078. ReadError: nil,
  1079. }
  1080. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  1081. connection := &Connection{
  1082. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", dataprovider.User{
  1083. BaseUser: sdk.BaseUser{
  1084. HomeDir: os.TempDir(),
  1085. },
  1086. }),
  1087. channel: &mockSSHChannel,
  1088. }
  1089. scpCommand := scpCommand{
  1090. sshCommand: sshCommand{
  1091. command: "scp",
  1092. connection: connection,
  1093. args: []string{"-t", "/tmp"},
  1094. },
  1095. }
  1096. _, _, err := scpCommand.parseUploadMessage(fs, "invalid")
  1097. assert.Error(t, err, "parsing invalid upload message must fail")
  1098. _, _, err = scpCommand.parseUploadMessage(fs, "D0755 0")
  1099. assert.Error(t, err, "parsing incomplete upload message must fail")
  1100. _, _, err = scpCommand.parseUploadMessage(fs, "D0755 invalidsize testdir")
  1101. assert.Error(t, err, "parsing upload message with invalid size must fail")
  1102. _, _, err = scpCommand.parseUploadMessage(fs, "D0755 0 ")
  1103. assert.Error(t, err, "parsing upload message with invalid name must fail")
  1104. }
  1105. func TestSCPProtocolMessages(t *testing.T) {
  1106. buf := make([]byte, 65535)
  1107. stdErrBuf := make([]byte, 65535)
  1108. readErr := fmt.Errorf("test read error")
  1109. writeErr := fmt.Errorf("test write error")
  1110. mockSSHChannel := MockChannel{
  1111. Buffer: bytes.NewBuffer(buf),
  1112. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1113. ReadError: readErr,
  1114. WriteError: writeErr,
  1115. }
  1116. connection := &Connection{
  1117. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", dataprovider.User{}),
  1118. channel: &mockSSHChannel,
  1119. }
  1120. scpCommand := scpCommand{
  1121. sshCommand: sshCommand{
  1122. command: "scp",
  1123. connection: connection,
  1124. args: []string{"-t", "/tmp"},
  1125. },
  1126. }
  1127. _, err := scpCommand.readProtocolMessage()
  1128. assert.EqualError(t, err, readErr.Error())
  1129. err = scpCommand.sendConfirmationMessage()
  1130. assert.EqualError(t, err, writeErr.Error())
  1131. err = scpCommand.sendProtocolMessage("E\n")
  1132. assert.EqualError(t, err, writeErr.Error())
  1133. _, err = scpCommand.getNextUploadProtocolMessage()
  1134. assert.EqualError(t, err, readErr.Error())
  1135. mockSSHChannel = MockChannel{
  1136. Buffer: bytes.NewBuffer([]byte("T1183832947 0 1183833773 0\n")),
  1137. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1138. ReadError: nil,
  1139. WriteError: writeErr,
  1140. }
  1141. scpCommand.connection.channel = &mockSSHChannel
  1142. _, err = scpCommand.getNextUploadProtocolMessage()
  1143. assert.EqualError(t, err, writeErr.Error())
  1144. respBuffer := []byte{0x02}
  1145. protocolErrorMsg := "protocol error msg"
  1146. respBuffer = append(respBuffer, protocolErrorMsg...)
  1147. respBuffer = append(respBuffer, 0x0A)
  1148. mockSSHChannel = MockChannel{
  1149. Buffer: bytes.NewBuffer(respBuffer),
  1150. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1151. ReadError: nil,
  1152. WriteError: nil,
  1153. }
  1154. scpCommand.connection.channel = &mockSSHChannel
  1155. err = scpCommand.readConfirmationMessage()
  1156. if assert.Error(t, err) {
  1157. assert.Equal(t, protocolErrorMsg, err.Error())
  1158. }
  1159. mockSSHChannel = MockChannel{
  1160. Buffer: bytes.NewBuffer(respBuffer),
  1161. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1162. ReadError: nil,
  1163. WriteError: writeErr,
  1164. }
  1165. scpCommand.connection.channel = &mockSSHChannel
  1166. err = scpCommand.downloadDirs(nil, nil)
  1167. assert.ErrorIs(t, err, writeErr)
  1168. }
  1169. func TestSCPTestDownloadProtocolMessages(t *testing.T) {
  1170. buf := make([]byte, 65535)
  1171. stdErrBuf := make([]byte, 65535)
  1172. readErr := fmt.Errorf("test read error")
  1173. writeErr := fmt.Errorf("test write error")
  1174. mockSSHChannel := MockChannel{
  1175. Buffer: bytes.NewBuffer(buf),
  1176. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1177. ReadError: readErr,
  1178. WriteError: writeErr,
  1179. }
  1180. connection := &Connection{
  1181. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", dataprovider.User{}),
  1182. channel: &mockSSHChannel,
  1183. }
  1184. scpCommand := scpCommand{
  1185. sshCommand: sshCommand{
  1186. command: "scp",
  1187. connection: connection,
  1188. args: []string{"-f", "-p", "/tmp"},
  1189. },
  1190. }
  1191. path := "testDir"
  1192. err := os.Mkdir(path, os.ModePerm)
  1193. assert.NoError(t, err)
  1194. stat, err := os.Stat(path)
  1195. assert.NoError(t, err)
  1196. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1197. assert.EqualError(t, err, writeErr.Error())
  1198. mockSSHChannel = MockChannel{
  1199. Buffer: bytes.NewBuffer(buf),
  1200. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1201. ReadError: readErr,
  1202. WriteError: nil,
  1203. }
  1204. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1205. assert.EqualError(t, err, readErr.Error())
  1206. mockSSHChannel = MockChannel{
  1207. Buffer: bytes.NewBuffer(buf),
  1208. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1209. ReadError: readErr,
  1210. WriteError: writeErr,
  1211. }
  1212. scpCommand.args = []string{"-f", "/tmp"}
  1213. scpCommand.connection.channel = &mockSSHChannel
  1214. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1215. assert.EqualError(t, err, writeErr.Error())
  1216. mockSSHChannel = MockChannel{
  1217. Buffer: bytes.NewBuffer(buf),
  1218. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1219. ReadError: readErr,
  1220. WriteError: nil,
  1221. }
  1222. scpCommand.connection.channel = &mockSSHChannel
  1223. err = scpCommand.sendDownloadProtocolMessages(path, stat)
  1224. assert.EqualError(t, err, readErr.Error())
  1225. err = os.Remove(path)
  1226. assert.NoError(t, err)
  1227. }
  1228. func TestSCPCommandHandleErrors(t *testing.T) {
  1229. buf := make([]byte, 65535)
  1230. stdErrBuf := make([]byte, 65535)
  1231. readErr := fmt.Errorf("test read error")
  1232. writeErr := fmt.Errorf("test write error")
  1233. mockSSHChannel := MockChannel{
  1234. Buffer: bytes.NewBuffer(buf),
  1235. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1236. ReadError: readErr,
  1237. WriteError: writeErr,
  1238. }
  1239. server, client := net.Pipe()
  1240. defer func() {
  1241. err := server.Close()
  1242. assert.NoError(t, err)
  1243. }()
  1244. defer func() {
  1245. err := client.Close()
  1246. assert.NoError(t, err)
  1247. }()
  1248. connection := &Connection{
  1249. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", dataprovider.User{}),
  1250. channel: &mockSSHChannel,
  1251. }
  1252. scpCommand := scpCommand{
  1253. sshCommand: sshCommand{
  1254. command: "scp",
  1255. connection: connection,
  1256. args: []string{"-f", "/tmp"},
  1257. },
  1258. }
  1259. err := scpCommand.handle()
  1260. assert.EqualError(t, err, readErr.Error())
  1261. scpCommand.args = []string{"-i", "/tmp"}
  1262. err = scpCommand.handle()
  1263. assert.Error(t, err, "invalid scp command must fail")
  1264. }
  1265. func TestSCPErrorsMockFs(t *testing.T) {
  1266. errFake := errors.New("fake error")
  1267. u := dataprovider.User{}
  1268. u.Username = "test"
  1269. u.Permissions = make(map[string][]string)
  1270. u.Permissions["/"] = []string{dataprovider.PermAny}
  1271. u.HomeDir = os.TempDir()
  1272. buf := make([]byte, 65535)
  1273. stdErrBuf := make([]byte, 65535)
  1274. mockSSHChannel := MockChannel{
  1275. Buffer: bytes.NewBuffer(buf),
  1276. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1277. }
  1278. server, client := net.Pipe()
  1279. defer func() {
  1280. err := server.Close()
  1281. assert.NoError(t, err)
  1282. }()
  1283. defer func() {
  1284. err := client.Close()
  1285. assert.NoError(t, err)
  1286. }()
  1287. connection := &Connection{
  1288. channel: &mockSSHChannel,
  1289. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", u),
  1290. }
  1291. scpCommand := scpCommand{
  1292. sshCommand: sshCommand{
  1293. command: "scp",
  1294. connection: connection,
  1295. args: []string{"-r", "-t", "/tmp"},
  1296. },
  1297. }
  1298. testfile := filepath.Join(u.HomeDir, "testfile")
  1299. err := os.WriteFile(testfile, []byte("test"), os.ModePerm)
  1300. assert.NoError(t, err)
  1301. fs := newMockOsFs(errFake, nil, true, "123", os.TempDir())
  1302. err = scpCommand.handleUploadFile(fs, testfile, testfile, 0, false, 4, "/testfile")
  1303. assert.NoError(t, err)
  1304. err = os.Remove(testfile)
  1305. assert.NoError(t, err)
  1306. }
  1307. func TestSCPRecursiveDownloadErrors(t *testing.T) {
  1308. buf := make([]byte, 65535)
  1309. stdErrBuf := make([]byte, 65535)
  1310. readErr := fmt.Errorf("test read error")
  1311. writeErr := fmt.Errorf("test write error")
  1312. mockSSHChannel := MockChannel{
  1313. Buffer: bytes.NewBuffer(buf),
  1314. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1315. ReadError: readErr,
  1316. WriteError: writeErr,
  1317. }
  1318. server, client := net.Pipe()
  1319. defer func() {
  1320. err := server.Close()
  1321. assert.NoError(t, err)
  1322. }()
  1323. defer func() {
  1324. err := client.Close()
  1325. assert.NoError(t, err)
  1326. }()
  1327. fs := vfs.NewOsFs("123", os.TempDir(), "", nil)
  1328. connection := &Connection{
  1329. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", dataprovider.User{
  1330. BaseUser: sdk.BaseUser{
  1331. HomeDir: os.TempDir(),
  1332. },
  1333. }),
  1334. channel: &mockSSHChannel,
  1335. }
  1336. scpCommand := scpCommand{
  1337. sshCommand: sshCommand{
  1338. command: "scp",
  1339. connection: connection,
  1340. args: []string{"-r", "-f", "/tmp"},
  1341. },
  1342. }
  1343. path := "testDir"
  1344. err := os.Mkdir(path, os.ModePerm)
  1345. assert.NoError(t, err)
  1346. stat, err := os.Stat(path)
  1347. assert.NoError(t, err)
  1348. err = scpCommand.handleRecursiveDownload(fs, "invalid_dir", "invalid_dir", stat)
  1349. assert.EqualError(t, err, writeErr.Error())
  1350. mockSSHChannel = MockChannel{
  1351. Buffer: bytes.NewBuffer(buf),
  1352. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1353. ReadError: nil,
  1354. WriteError: nil,
  1355. }
  1356. scpCommand.connection.channel = &mockSSHChannel
  1357. err = scpCommand.handleRecursiveDownload(fs, "invalid_dir", "invalid_dir", stat)
  1358. assert.Error(t, err, "recursive upload download must fail for a non existing dir")
  1359. err = os.Remove(path)
  1360. assert.NoError(t, err)
  1361. }
  1362. func TestSCPRecursiveUploadErrors(t *testing.T) {
  1363. buf := make([]byte, 65535)
  1364. stdErrBuf := make([]byte, 65535)
  1365. readErr := fmt.Errorf("test read error")
  1366. writeErr := fmt.Errorf("test write error")
  1367. mockSSHChannel := MockChannel{
  1368. Buffer: bytes.NewBuffer(buf),
  1369. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1370. ReadError: readErr,
  1371. WriteError: writeErr,
  1372. }
  1373. connection := &Connection{
  1374. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", dataprovider.User{}),
  1375. channel: &mockSSHChannel,
  1376. }
  1377. scpCommand := scpCommand{
  1378. sshCommand: sshCommand{
  1379. command: "scp",
  1380. connection: connection,
  1381. args: []string{"-r", "-t", "/tmp"},
  1382. },
  1383. }
  1384. err := scpCommand.handleRecursiveUpload()
  1385. assert.Error(t, err, "recursive upload must fail, we send a fake error message")
  1386. mockSSHChannel = MockChannel{
  1387. Buffer: bytes.NewBuffer(buf),
  1388. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1389. ReadError: readErr,
  1390. WriteError: nil,
  1391. }
  1392. scpCommand.connection.channel = &mockSSHChannel
  1393. err = scpCommand.handleRecursiveUpload()
  1394. assert.Error(t, err, "recursive upload must fail, we send a fake error message")
  1395. }
  1396. func TestSCPCreateDirs(t *testing.T) {
  1397. buf := make([]byte, 65535)
  1398. stdErrBuf := make([]byte, 65535)
  1399. u := dataprovider.User{}
  1400. u.HomeDir = "home_rel_path"
  1401. u.Username = "test"
  1402. u.Permissions = make(map[string][]string)
  1403. u.Permissions["/"] = []string{dataprovider.PermAny}
  1404. mockSSHChannel := MockChannel{
  1405. Buffer: bytes.NewBuffer(buf),
  1406. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1407. ReadError: nil,
  1408. WriteError: nil,
  1409. }
  1410. fs, err := u.GetFilesystem("123")
  1411. assert.NoError(t, err)
  1412. connection := &Connection{
  1413. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", u),
  1414. channel: &mockSSHChannel,
  1415. }
  1416. scpCommand := scpCommand{
  1417. sshCommand: sshCommand{
  1418. command: "scp",
  1419. connection: connection,
  1420. args: []string{"-r", "-t", "/tmp"},
  1421. },
  1422. }
  1423. err = scpCommand.handleCreateDir(fs, "invalid_dir")
  1424. assert.Error(t, err, "create invalid dir must fail")
  1425. }
  1426. func TestSCPDownloadFileData(t *testing.T) {
  1427. testfile := "testfile"
  1428. buf := make([]byte, 65535)
  1429. readErr := fmt.Errorf("test read error")
  1430. writeErr := fmt.Errorf("test write error")
  1431. stdErrBuf := make([]byte, 65535)
  1432. mockSSHChannelReadErr := MockChannel{
  1433. Buffer: bytes.NewBuffer(buf),
  1434. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1435. ReadError: readErr,
  1436. WriteError: nil,
  1437. }
  1438. mockSSHChannelWriteErr := MockChannel{
  1439. Buffer: bytes.NewBuffer(buf),
  1440. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1441. ReadError: nil,
  1442. WriteError: writeErr,
  1443. }
  1444. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  1445. connection := &Connection{
  1446. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", dataprovider.User{BaseUser: sdk.BaseUser{HomeDir: os.TempDir()}}),
  1447. channel: &mockSSHChannelReadErr,
  1448. }
  1449. scpCommand := scpCommand{
  1450. sshCommand: sshCommand{
  1451. command: "scp",
  1452. connection: connection,
  1453. args: []string{"-r", "-f", "/tmp"},
  1454. },
  1455. }
  1456. err := os.WriteFile(testfile, []byte("test"), os.ModePerm)
  1457. assert.NoError(t, err)
  1458. stat, err := os.Stat(testfile)
  1459. assert.NoError(t, err)
  1460. err = scpCommand.sendDownloadFileData(fs, testfile, stat, nil)
  1461. assert.EqualError(t, err, readErr.Error())
  1462. scpCommand.connection.channel = &mockSSHChannelWriteErr
  1463. err = scpCommand.sendDownloadFileData(fs, testfile, stat, nil)
  1464. assert.EqualError(t, err, writeErr.Error())
  1465. scpCommand.args = []string{"-r", "-p", "-f", "/tmp"}
  1466. err = scpCommand.sendDownloadFileData(fs, testfile, stat, nil)
  1467. assert.EqualError(t, err, writeErr.Error())
  1468. scpCommand.connection.channel = &mockSSHChannelReadErr
  1469. err = scpCommand.sendDownloadFileData(fs, testfile, stat, nil)
  1470. assert.EqualError(t, err, readErr.Error())
  1471. err = os.Remove(testfile)
  1472. assert.NoError(t, err)
  1473. }
  1474. func TestSCPUploadFiledata(t *testing.T) {
  1475. testfile := "testfile"
  1476. buf := make([]byte, 65535)
  1477. stdErrBuf := make([]byte, 65535)
  1478. readErr := fmt.Errorf("test read error")
  1479. writeErr := fmt.Errorf("test write error")
  1480. mockSSHChannel := MockChannel{
  1481. Buffer: bytes.NewBuffer(buf),
  1482. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1483. ReadError: readErr,
  1484. WriteError: writeErr,
  1485. }
  1486. user := dataprovider.User{
  1487. BaseUser: sdk.BaseUser{
  1488. Username: "testuser",
  1489. },
  1490. }
  1491. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  1492. connection := &Connection{
  1493. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", user),
  1494. channel: &mockSSHChannel,
  1495. }
  1496. scpCommand := scpCommand{
  1497. sshCommand: sshCommand{
  1498. command: "scp",
  1499. connection: connection,
  1500. args: []string{"-r", "-t", "/tmp"},
  1501. },
  1502. }
  1503. file, err := os.Create(testfile)
  1504. assert.NoError(t, err)
  1505. baseTransfer := common.NewBaseTransfer(file, scpCommand.connection.BaseConnection, nil, file.Name(), file.Name(),
  1506. "/"+testfile, common.TransferDownload, 0, 0, 0, 0, true, fs, dataprovider.TransferQuota{})
  1507. transfer := newTransfer(baseTransfer, nil, nil, nil)
  1508. err = scpCommand.getUploadFileData(2, transfer)
  1509. assert.Error(t, err, "upload must fail, we send a fake write error message")
  1510. mockSSHChannel = MockChannel{
  1511. Buffer: bytes.NewBuffer(buf),
  1512. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1513. ReadError: readErr,
  1514. WriteError: nil,
  1515. }
  1516. scpCommand.connection.channel = &mockSSHChannel
  1517. file, err = os.Create(testfile)
  1518. assert.NoError(t, err)
  1519. transfer.File = file
  1520. transfer.isFinished = false
  1521. transfer.Connection.AddTransfer(transfer)
  1522. err = scpCommand.getUploadFileData(2, transfer)
  1523. assert.Error(t, err, "upload must fail, we send a fake read error message")
  1524. respBuffer := []byte("12")
  1525. respBuffer = append(respBuffer, 0x02)
  1526. mockSSHChannel = MockChannel{
  1527. Buffer: bytes.NewBuffer(respBuffer),
  1528. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1529. ReadError: nil,
  1530. WriteError: nil,
  1531. }
  1532. scpCommand.connection.channel = &mockSSHChannel
  1533. file, err = os.Create(testfile)
  1534. assert.NoError(t, err)
  1535. baseTransfer.File = file
  1536. transfer = newTransfer(baseTransfer, nil, nil, nil)
  1537. transfer.Connection.AddTransfer(transfer)
  1538. err = scpCommand.getUploadFileData(2, transfer)
  1539. assert.Error(t, err, "upload must fail, we have not enough data to read")
  1540. // the file is already closed so we have an error on trasfer closing
  1541. mockSSHChannel = MockChannel{
  1542. Buffer: bytes.NewBuffer(buf),
  1543. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1544. ReadError: nil,
  1545. WriteError: nil,
  1546. }
  1547. transfer.Connection.AddTransfer(transfer)
  1548. err = scpCommand.getUploadFileData(0, transfer)
  1549. if assert.Error(t, err) {
  1550. assert.EqualError(t, err, common.ErrTransferClosed.Error())
  1551. }
  1552. mockSSHChannel = MockChannel{
  1553. Buffer: bytes.NewBuffer(buf),
  1554. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1555. ReadError: nil,
  1556. WriteError: nil,
  1557. }
  1558. transfer.Connection.AddTransfer(transfer)
  1559. err = scpCommand.getUploadFileData(2, transfer)
  1560. assert.ErrorContains(t, err, os.ErrClosed.Error())
  1561. err = os.Remove(testfile)
  1562. assert.NoError(t, err)
  1563. }
  1564. func TestUploadError(t *testing.T) {
  1565. common.Config.UploadMode = common.UploadModeAtomic
  1566. user := dataprovider.User{
  1567. BaseUser: sdk.BaseUser{
  1568. Username: "testuser",
  1569. },
  1570. }
  1571. fs := vfs.NewOsFs("", os.TempDir(), "", nil)
  1572. connection := &Connection{
  1573. BaseConnection: common.NewBaseConnection("", common.ProtocolSCP, "", "", user),
  1574. }
  1575. testfile := "testfile"
  1576. fileTempName := "temptestfile"
  1577. file, err := os.Create(fileTempName)
  1578. assert.NoError(t, err)
  1579. baseTransfer := common.NewBaseTransfer(file, connection.BaseConnection, nil, testfile, file.Name(),
  1580. testfile, common.TransferUpload, 0, 0, 0, 0, true, fs, dataprovider.TransferQuota{})
  1581. transfer := newTransfer(baseTransfer, nil, nil, nil)
  1582. errFake := errors.New("fake error")
  1583. transfer.TransferError(errFake)
  1584. err = transfer.Close()
  1585. if assert.Error(t, err) {
  1586. assert.EqualError(t, err, common.ErrGenericFailure.Error())
  1587. }
  1588. if assert.Error(t, transfer.ErrTransfer) {
  1589. assert.EqualError(t, transfer.ErrTransfer, errFake.Error())
  1590. }
  1591. assert.Equal(t, int64(0), transfer.BytesReceived.Load())
  1592. assert.NoFileExists(t, testfile)
  1593. assert.NoFileExists(t, fileTempName)
  1594. common.Config.UploadMode = common.UploadModeAtomicWithResume
  1595. }
  1596. func TestTransferFailingReader(t *testing.T) {
  1597. user := dataprovider.User{
  1598. BaseUser: sdk.BaseUser{
  1599. Username: "testuser",
  1600. HomeDir: os.TempDir(),
  1601. },
  1602. FsConfig: vfs.Filesystem{
  1603. Provider: sdk.CryptedFilesystemProvider,
  1604. CryptConfig: vfs.CryptFsConfig{
  1605. Passphrase: kms.NewPlainSecret("crypt secret"),
  1606. },
  1607. },
  1608. }
  1609. user.Permissions = make(map[string][]string)
  1610. user.Permissions["/"] = []string{dataprovider.PermAny}
  1611. fs := newMockOsFs(nil, nil, true, "", os.TempDir())
  1612. connection := &Connection{
  1613. BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
  1614. }
  1615. request := sftp.NewRequest("Open", "afile.txt")
  1616. request.Flags = 27 // read,write,create,truncate
  1617. transfer, err := connection.handleFilewrite(request)
  1618. require.NoError(t, err)
  1619. buf := make([]byte, 32)
  1620. _, err = transfer.ReadAt(buf, 0)
  1621. assert.ErrorIs(t, err, sftp.ErrSSHFxOpUnsupported)
  1622. if c, ok := transfer.(io.Closer); ok {
  1623. err = c.Close()
  1624. assert.NoError(t, err)
  1625. }
  1626. fsPath := filepath.Join(os.TempDir(), "afile.txt")
  1627. r, _, err := pipeat.Pipe()
  1628. assert.NoError(t, err)
  1629. baseTransfer := common.NewBaseTransfer(nil, connection.BaseConnection, nil, fsPath, fsPath, filepath.Base(fsPath),
  1630. common.TransferUpload, 0, 0, 0, 0, false, fs, dataprovider.TransferQuota{})
  1631. errRead := errors.New("read is not allowed")
  1632. tr := newTransfer(baseTransfer, nil, vfs.NewPipeReader(r), errRead)
  1633. _, err = tr.ReadAt(buf, 0)
  1634. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  1635. err = tr.Close()
  1636. assert.NoError(t, err)
  1637. tr = newTransfer(baseTransfer, nil, nil, errRead)
  1638. _, err = tr.ReadAt(buf, 0)
  1639. assert.ErrorIs(t, err, sftp.ErrSSHFxFailure)
  1640. err = tr.Close()
  1641. assert.NoError(t, err)
  1642. err = os.Remove(fsPath)
  1643. assert.NoError(t, err)
  1644. assert.Len(t, connection.GetTransfers(), 0)
  1645. }
  1646. func TestConfigsFromProvider(t *testing.T) {
  1647. err := dataprovider.UpdateConfigs(nil, "", "", "")
  1648. assert.NoError(t, err)
  1649. c := Configuration{}
  1650. err = c.loadFromProvider()
  1651. assert.NoError(t, err)
  1652. assert.Len(t, c.HostKeyAlgorithms, 0)
  1653. assert.Len(t, c.KexAlgorithms, 0)
  1654. assert.Len(t, c.Ciphers, 0)
  1655. assert.Len(t, c.MACs, 0)
  1656. assert.Len(t, c.PublicKeyAlgorithms, 0)
  1657. configs := dataprovider.Configs{
  1658. SFTPD: &dataprovider.SFTPDConfigs{
  1659. HostKeyAlgos: []string{ssh.KeyAlgoRSA},
  1660. KexAlgorithms: []string{ssh.InsecureKeyExchangeDHGEXSHA1},
  1661. Ciphers: []string{ssh.InsecureCipherAES128CBC, ssh.InsecureCipherAES192CBC, ssh.InsecureCipherAES256CBC},
  1662. MACs: []string{ssh.HMACSHA512ETM},
  1663. PublicKeyAlgos: []string{ssh.InsecureKeyAlgoDSA},
  1664. },
  1665. }
  1666. err = dataprovider.UpdateConfigs(&configs, "", "", "")
  1667. assert.NoError(t, err)
  1668. err = c.loadFromProvider()
  1669. assert.NoError(t, err)
  1670. expectedHostKeyAlgos := append(preferredHostKeyAlgos, configs.SFTPD.HostKeyAlgos...)
  1671. expectedKEXs := append(preferredKexAlgos, configs.SFTPD.KexAlgorithms...)
  1672. expectedCiphers := append(preferredCiphers, configs.SFTPD.Ciphers...)
  1673. expectedMACs := append(preferredMACs, configs.SFTPD.MACs...)
  1674. expectedPublicKeyAlgos := append(preferredPublicKeyAlgos, configs.SFTPD.PublicKeyAlgos...)
  1675. assert.Equal(t, expectedHostKeyAlgos, c.HostKeyAlgorithms)
  1676. assert.Equal(t, expectedKEXs, c.KexAlgorithms)
  1677. assert.Equal(t, expectedCiphers, c.Ciphers)
  1678. assert.Equal(t, expectedMACs, c.MACs)
  1679. assert.Equal(t, expectedPublicKeyAlgos, c.PublicKeyAlgorithms)
  1680. err = dataprovider.UpdateConfigs(nil, "", "", "")
  1681. assert.NoError(t, err)
  1682. }
  1683. func TestSupportedSecurityOptions(t *testing.T) {
  1684. c := Configuration{
  1685. KexAlgorithms: supportedKexAlgos,
  1686. MACs: supportedMACs,
  1687. Ciphers: supportedCiphers,
  1688. }
  1689. var defaultKexs []string
  1690. for _, k := range supportedKexAlgos {
  1691. defaultKexs = append(defaultKexs, k)
  1692. if k == ssh.KeyExchangeCurve25519SHA256 {
  1693. defaultKexs = append(defaultKexs, keyExchangeCurve25519SHA256LibSSH)
  1694. }
  1695. }
  1696. serverConfig := &ssh.ServerConfig{}
  1697. err := c.configureSecurityOptions(serverConfig)
  1698. assert.NoError(t, err)
  1699. assert.Equal(t, supportedCiphers, serverConfig.Ciphers)
  1700. assert.Equal(t, supportedMACs, serverConfig.MACs)
  1701. assert.Equal(t, defaultKexs, serverConfig.KeyExchanges)
  1702. c.KexAlgorithms = append(c.KexAlgorithms, "not a kex")
  1703. err = c.configureSecurityOptions(serverConfig)
  1704. assert.Error(t, err)
  1705. c.KexAlgorithms = append(supportedKexAlgos, "diffie-hellman-group18-sha512")
  1706. c.MACs = []string{
  1707. " [email protected] ", " [email protected]",
  1708. "hmac-sha2-256", "hmac-sha2-512 ",
  1709. " hmac-sha1-96", "hmac-sha1 ",
  1710. }
  1711. err = c.configureSecurityOptions(serverConfig)
  1712. assert.NoError(t, err)
  1713. assert.Equal(t, supportedCiphers, serverConfig.Ciphers)
  1714. assert.Equal(t, supportedMACs, serverConfig.MACs)
  1715. assert.Equal(t, defaultKexs, serverConfig.KeyExchanges)
  1716. }
  1717. func TestLoadHostKeys(t *testing.T) {
  1718. serverConfig := &ssh.ServerConfig{}
  1719. c := Configuration{}
  1720. c.HostKeys = []string{".", "missing file"}
  1721. err := c.checkAndLoadHostKeys(configDir, serverConfig)
  1722. assert.Error(t, err)
  1723. testfile := filepath.Join(os.TempDir(), "invalidkey")
  1724. err = os.WriteFile(testfile, []byte("some bytes"), os.ModePerm)
  1725. assert.NoError(t, err)
  1726. c.HostKeys = []string{testfile}
  1727. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1728. assert.Error(t, err)
  1729. err = os.Remove(testfile)
  1730. assert.NoError(t, err)
  1731. keysDir := filepath.Join(os.TempDir(), "keys")
  1732. err = os.MkdirAll(keysDir, os.ModePerm)
  1733. assert.NoError(t, err)
  1734. rsaKeyName := filepath.Join(keysDir, defaultPrivateRSAKeyName)
  1735. ecdsaKeyName := filepath.Join(keysDir, defaultPrivateECDSAKeyName)
  1736. ed25519KeyName := filepath.Join(keysDir, defaultPrivateEd25519KeyName)
  1737. nonDefaultKeyName := filepath.Join(keysDir, "akey")
  1738. c.HostKeys = []string{nonDefaultKeyName, rsaKeyName, ecdsaKeyName, ed25519KeyName}
  1739. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1740. assert.Error(t, err)
  1741. c.HostKeyAlgorithms = []string{ssh.KeyAlgoRSASHA256}
  1742. c.HostKeys = []string{ecdsaKeyName}
  1743. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1744. assert.Error(t, err)
  1745. c.HostKeyAlgorithms = preferredHostKeyAlgos
  1746. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1747. assert.NoError(t, err)
  1748. assert.FileExists(t, rsaKeyName)
  1749. assert.FileExists(t, ecdsaKeyName)
  1750. assert.FileExists(t, ed25519KeyName)
  1751. assert.NoFileExists(t, nonDefaultKeyName)
  1752. err = os.Remove(rsaKeyName)
  1753. assert.NoError(t, err)
  1754. err = os.Remove(ecdsaKeyName)
  1755. assert.NoError(t, err)
  1756. err = os.Remove(ed25519KeyName)
  1757. assert.NoError(t, err)
  1758. if runtime.GOOS != osWindows {
  1759. err = os.Chmod(keysDir, 0551)
  1760. assert.NoError(t, err)
  1761. c.HostKeys = nil
  1762. err = c.checkAndLoadHostKeys(keysDir, serverConfig)
  1763. assert.Error(t, err)
  1764. c.HostKeys = []string{rsaKeyName, ecdsaKeyName}
  1765. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1766. assert.Error(t, err)
  1767. c.HostKeys = []string{ecdsaKeyName, rsaKeyName}
  1768. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1769. assert.Error(t, err)
  1770. c.HostKeys = []string{ed25519KeyName}
  1771. err = c.checkAndLoadHostKeys(configDir, serverConfig)
  1772. assert.Error(t, err)
  1773. err = os.Chmod(keysDir, 0755)
  1774. assert.NoError(t, err)
  1775. }
  1776. err = os.RemoveAll(keysDir)
  1777. assert.NoError(t, err)
  1778. }
  1779. func TestCertCheckerInitErrors(t *testing.T) {
  1780. c := Configuration{}
  1781. c.TrustedUserCAKeys = []string{".", "missing file"}
  1782. err := c.initializeCertChecker("")
  1783. assert.Error(t, err)
  1784. testfile := filepath.Join(os.TempDir(), "invalidkey")
  1785. err = os.WriteFile(testfile, []byte("some bytes"), os.ModePerm)
  1786. assert.NoError(t, err)
  1787. c.TrustedUserCAKeys = []string{testfile}
  1788. err = c.initializeCertChecker("")
  1789. assert.Error(t, err)
  1790. err = os.Remove(testfile)
  1791. assert.NoError(t, err)
  1792. }
  1793. func TestSFTPSubSystem(t *testing.T) {
  1794. permissions := make(map[string][]string)
  1795. permissions["/"] = []string{dataprovider.PermAny}
  1796. user := &dataprovider.User{
  1797. BaseUser: sdk.BaseUser{
  1798. Permissions: permissions,
  1799. HomeDir: os.TempDir(),
  1800. },
  1801. }
  1802. user.FsConfig.Provider = sdk.AzureBlobFilesystemProvider
  1803. err := ServeSubSystemConnection(user, "connID", nil, nil)
  1804. assert.Error(t, err)
  1805. user.FsConfig.Provider = sdk.LocalFilesystemProvider
  1806. buf := make([]byte, 0, 4096)
  1807. stdErrBuf := make([]byte, 0, 4096)
  1808. mockSSHChannel := &MockChannel{
  1809. Buffer: bytes.NewBuffer(buf),
  1810. StdErrBuffer: bytes.NewBuffer(stdErrBuf),
  1811. }
  1812. // this is 327680 and it will result in packet too long error
  1813. _, err = mockSSHChannel.Write([]byte{0x00, 0x05, 0x00, 0x00, 0x00, 0x00})
  1814. assert.NoError(t, err)
  1815. err = ServeSubSystemConnection(user, "id", mockSSHChannel, mockSSHChannel)
  1816. assert.EqualError(t, err, "packet too long")
  1817. subsystemChannel := newSubsystemChannel(mockSSHChannel, mockSSHChannel)
  1818. n, err := subsystemChannel.Write([]byte{0x00})
  1819. assert.NoError(t, err)
  1820. assert.Equal(t, n, 1)
  1821. err = subsystemChannel.Close()
  1822. assert.NoError(t, err)
  1823. }
  1824. func TestRecoverer(t *testing.T) {
  1825. c := Configuration{}
  1826. c.AcceptInboundConnection(nil, nil)
  1827. connID := "connectionID"
  1828. connection := &Connection{
  1829. BaseConnection: common.NewBaseConnection(connID, common.ProtocolSFTP, "", "", dataprovider.User{}),
  1830. }
  1831. c.handleSftpConnection(nil, connection)
  1832. sshCmd := sshCommand{
  1833. command: "cd",
  1834. connection: connection,
  1835. }
  1836. err := sshCmd.handle()
  1837. assert.EqualError(t, err, common.ErrGenericFailure.Error())
  1838. scpCmd := scpCommand{
  1839. sshCommand: sshCommand{
  1840. command: "scp",
  1841. connection: connection,
  1842. },
  1843. }
  1844. err = scpCmd.handle()
  1845. assert.EqualError(t, err, common.ErrGenericFailure.Error())
  1846. assert.Len(t, common.Connections.GetStats(""), 0)
  1847. }
  1848. func TestListernerAcceptErrors(t *testing.T) {
  1849. errFake := errors.New("a fake error")
  1850. listener := newFakeListener(errFake)
  1851. c := Configuration{}
  1852. err := c.serve(listener, nil)
  1853. require.EqualError(t, err, errFake.Error())
  1854. err = listener.Close()
  1855. require.NoError(t, err)
  1856. errNetFake := &fakeNetError{error: errFake}
  1857. listener = newFakeListener(errNetFake)
  1858. err = c.serve(listener, nil)
  1859. require.EqualError(t, err, errFake.Error())
  1860. err = listener.Close()
  1861. require.NoError(t, err)
  1862. }
  1863. type fakeNetError struct {
  1864. error
  1865. count int
  1866. }
  1867. func (e *fakeNetError) Timeout() bool {
  1868. return false
  1869. }
  1870. func (e *fakeNetError) Temporary() bool {
  1871. e.count++
  1872. return e.count < 10
  1873. }
  1874. func (e *fakeNetError) Error() string {
  1875. return e.error.Error()
  1876. }
  1877. type fakeListener struct {
  1878. server net.Conn
  1879. client net.Conn
  1880. err error
  1881. }
  1882. func (l *fakeListener) Accept() (net.Conn, error) {
  1883. return l.client, l.err
  1884. }
  1885. func (l *fakeListener) Close() error {
  1886. errClient := l.client.Close()
  1887. errServer := l.server.Close()
  1888. if errServer != nil {
  1889. return errServer
  1890. }
  1891. return errClient
  1892. }
  1893. func (l *fakeListener) Addr() net.Addr {
  1894. return l.server.LocalAddr()
  1895. }
  1896. func newFakeListener(err error) net.Listener {
  1897. server, client := net.Pipe()
  1898. return &fakeListener{
  1899. server: server,
  1900. client: client,
  1901. err: err,
  1902. }
  1903. }
  1904. func TestLoadRevokedUserCertsFile(t *testing.T) {
  1905. r := revokedCertificates{
  1906. certs: map[string]bool{},
  1907. }
  1908. err := r.load()
  1909. assert.NoError(t, err)
  1910. r.filePath = filepath.Join(os.TempDir(), "sub", "testrevoked")
  1911. err = os.MkdirAll(filepath.Dir(r.filePath), os.ModePerm)
  1912. assert.NoError(t, err)
  1913. err = os.WriteFile(r.filePath, []byte(`no json`), 0644)
  1914. assert.NoError(t, err)
  1915. err = r.load()
  1916. assert.Error(t, err)
  1917. r.filePath = filepath.Dir(r.filePath)
  1918. err = r.load()
  1919. assert.Error(t, err)
  1920. err = os.RemoveAll(r.filePath)
  1921. assert.NoError(t, err)
  1922. }
  1923. func TestMaxUserSessions(t *testing.T) {
  1924. connection := &Connection{
  1925. BaseConnection: common.NewBaseConnection(xid.New().String(), common.ProtocolSFTP, "", "", dataprovider.User{
  1926. BaseUser: sdk.BaseUser{
  1927. Username: "user_max_sessions",
  1928. HomeDir: filepath.Clean(os.TempDir()),
  1929. MaxSessions: 1,
  1930. },
  1931. }),
  1932. }
  1933. err := common.Connections.Add(connection)
  1934. assert.NoError(t, err)
  1935. c := Configuration{}
  1936. c.handleSftpConnection(nil, connection)
  1937. sshCmd := sshCommand{
  1938. command: "cd",
  1939. connection: connection,
  1940. }
  1941. err = sshCmd.handle()
  1942. if assert.Error(t, err) {
  1943. assert.Contains(t, err.Error(), "too many open sessions")
  1944. }
  1945. scpCmd := scpCommand{
  1946. sshCommand: sshCommand{
  1947. command: "scp",
  1948. connection: connection,
  1949. },
  1950. }
  1951. err = scpCmd.handle()
  1952. if assert.Error(t, err) {
  1953. assert.Contains(t, err.Error(), "too many open sessions")
  1954. }
  1955. err = ServeSubSystemConnection(&connection.User, connection.ID, nil, nil)
  1956. if assert.Error(t, err) {
  1957. assert.Contains(t, err.Error(), "too many open sessions")
  1958. }
  1959. common.Connections.Remove(connection.GetID())
  1960. assert.Len(t, common.Connections.GetStats(""), 0)
  1961. }
  1962. func TestCanReadSymlink(t *testing.T) {
  1963. connection := &Connection{
  1964. BaseConnection: common.NewBaseConnection(xid.New().String(), common.ProtocolSFTP, "", "", dataprovider.User{
  1965. BaseUser: sdk.BaseUser{
  1966. Username: "user_can_read_symlink",
  1967. HomeDir: filepath.Clean(os.TempDir()),
  1968. Permissions: map[string][]string{
  1969. "/": {dataprovider.PermAny},
  1970. "/sub": {dataprovider.PermUpload},
  1971. },
  1972. },
  1973. Filters: dataprovider.UserFilters{
  1974. BaseUserFilters: sdk.BaseUserFilters{
  1975. FilePatterns: []sdk.PatternsFilter{
  1976. {
  1977. Path: "/denied",
  1978. DeniedPatterns: []string{"*.txt"},
  1979. DenyPolicy: sdk.DenyPolicyHide,
  1980. },
  1981. },
  1982. },
  1983. },
  1984. }),
  1985. }
  1986. err := connection.canReadLink("/sub/link")
  1987. assert.ErrorIs(t, err, sftp.ErrSSHFxPermissionDenied)
  1988. err = connection.canReadLink("/denied/file.txt")
  1989. assert.ErrorIs(t, err, sftp.ErrSSHFxNoSuchFile)
  1990. }
  1991. func TestAuthenticationErrors(t *testing.T) {
  1992. loginMethod := dataprovider.SSHLoginMethodPassword
  1993. username := "test user"
  1994. err := newAuthenticationError(fmt.Errorf("cannot validate credentials: %w", util.NewRecordNotFoundError("not found")),
  1995. loginMethod, username)
  1996. assert.ErrorIs(t, err, sftpAuthError)
  1997. assert.ErrorIs(t, err, util.ErrNotFound)
  1998. var sftpAuthErr *authenticationError
  1999. if assert.ErrorAs(t, err, &sftpAuthErr) {
  2000. assert.Equal(t, loginMethod, sftpAuthErr.getLoginMethod())
  2001. assert.Equal(t, username, sftpAuthErr.getUsername())
  2002. }
  2003. err = newAuthenticationError(fmt.Errorf("cannot validate credentials: %w", fs.ErrPermission), loginMethod, username)
  2004. assert.ErrorIs(t, err, sftpAuthError)
  2005. assert.NotErrorIs(t, err, util.ErrNotFound)
  2006. err = newAuthenticationError(fmt.Errorf("cert has wrong type %d", ssh.HostCert), loginMethod, username)
  2007. assert.ErrorIs(t, err, sftpAuthError)
  2008. assert.NotErrorIs(t, err, util.ErrNotFound)
  2009. err = newAuthenticationError(errors.New("ssh: certificate signed by unrecognized authority"), loginMethod, username)
  2010. assert.ErrorIs(t, err, sftpAuthError)
  2011. assert.NotErrorIs(t, err, util.ErrNotFound)
  2012. err = newAuthenticationError(nil, loginMethod, username)
  2013. assert.ErrorIs(t, err, sftpAuthError)
  2014. assert.NotErrorIs(t, err, util.ErrNotFound)
  2015. }