portable.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  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. //go:build !noportable
  15. // +build !noportable
  16. package cmd
  17. import (
  18. "fmt"
  19. "os"
  20. "path"
  21. "path/filepath"
  22. "strings"
  23. "github.com/sftpgo/sdk"
  24. "github.com/spf13/cobra"
  25. "github.com/drakkan/sftpgo/v2/internal/common"
  26. "github.com/drakkan/sftpgo/v2/internal/dataprovider"
  27. "github.com/drakkan/sftpgo/v2/internal/kms"
  28. "github.com/drakkan/sftpgo/v2/internal/service"
  29. "github.com/drakkan/sftpgo/v2/internal/sftpd"
  30. "github.com/drakkan/sftpgo/v2/internal/util"
  31. "github.com/drakkan/sftpgo/v2/internal/version"
  32. "github.com/drakkan/sftpgo/v2/internal/vfs"
  33. )
  34. var (
  35. directoryToServe string
  36. portableSFTPDPort int
  37. portableUsername string
  38. portablePassword string
  39. portablePasswordFile string
  40. portableStartDir string
  41. portableLogFile string
  42. portableLogLevel string
  43. portableLogUTCTime bool
  44. portablePublicKeys []string
  45. portablePermissions []string
  46. portableSSHCommands []string
  47. portableAllowedPatterns []string
  48. portableDeniedPatterns []string
  49. portableFsProvider string
  50. portableS3Bucket string
  51. portableS3Region string
  52. portableS3AccessKey string
  53. portableS3AccessSecret string
  54. portableS3RoleARN string
  55. portableS3Endpoint string
  56. portableS3StorageClass string
  57. portableS3ACL string
  58. portableS3KeyPrefix string
  59. portableS3ULPartSize int
  60. portableS3ULConcurrency int
  61. portableS3ForcePathStyle bool
  62. portableS3SkipTLSVerify bool
  63. portableGCSBucket string
  64. portableGCSCredentialsFile string
  65. portableGCSAutoCredentials int
  66. portableGCSStorageClass string
  67. portableGCSKeyPrefix string
  68. portableFTPDPort int
  69. portableFTPSCert string
  70. portableFTPSKey string
  71. portableWebDAVPort int
  72. portableWebDAVCert string
  73. portableWebDAVKey string
  74. portableHTTPPort int
  75. portableHTTPSCert string
  76. portableHTTPSKey string
  77. portableAzContainer string
  78. portableAzAccountName string
  79. portableAzAccountKey string
  80. portableAzEndpoint string
  81. portableAzAccessTier string
  82. portableAzSASURL string
  83. portableAzKeyPrefix string
  84. portableAzULPartSize int
  85. portableAzULConcurrency int
  86. portableAzDLPartSize int
  87. portableAzDLConcurrency int
  88. portableAzUseEmulator bool
  89. portableCryptPassphrase string
  90. portableSFTPEndpoint string
  91. portableSFTPUsername string
  92. portableSFTPPassword string
  93. portableSFTPPrivateKeyPath string
  94. portableSFTPFingerprints []string
  95. portableSFTPPrefix string
  96. portableSFTPDisableConcurrentReads bool
  97. portableSFTPDBufferSize int64
  98. portableCmd = &cobra.Command{
  99. Use: "portable",
  100. Short: "Serve a single directory/account",
  101. Long: `To serve the current working directory with auto generated credentials simply
  102. use:
  103. $ sftpgo portable
  104. Please take a look at the usage below to customize the serving parameters`,
  105. Run: func(_ *cobra.Command, _ []string) {
  106. portableDir := directoryToServe
  107. fsProvider := sdk.GetProviderByName(portableFsProvider)
  108. if !filepath.IsAbs(portableDir) {
  109. if fsProvider == sdk.LocalFilesystemProvider {
  110. portableDir, _ = filepath.Abs(portableDir)
  111. } else {
  112. portableDir = os.TempDir()
  113. }
  114. }
  115. permissions := make(map[string][]string)
  116. permissions["/"] = portablePermissions
  117. portableGCSCredentials := ""
  118. if fsProvider == sdk.GCSFilesystemProvider && portableGCSCredentialsFile != "" {
  119. contents, err := getFileContents(portableGCSCredentialsFile)
  120. if err != nil {
  121. fmt.Printf("Unable to get GCS credentials: %v\n", err)
  122. os.Exit(1)
  123. }
  124. portableGCSCredentials = contents
  125. portableGCSAutoCredentials = 0
  126. }
  127. portableSFTPPrivateKey := ""
  128. if fsProvider == sdk.SFTPFilesystemProvider && portableSFTPPrivateKeyPath != "" {
  129. contents, err := getFileContents(portableSFTPPrivateKeyPath)
  130. if err != nil {
  131. fmt.Printf("Unable to get SFTP private key: %v\n", err)
  132. os.Exit(1)
  133. }
  134. portableSFTPPrivateKey = contents
  135. }
  136. if portableFTPDPort >= 0 && portableFTPSCert != "" && portableFTPSKey != "" {
  137. keyPairs := []common.TLSKeyPair{
  138. {
  139. Cert: portableFTPSCert,
  140. Key: portableFTPSKey,
  141. ID: common.DefaultTLSKeyPaidID,
  142. },
  143. }
  144. _, err := common.NewCertManager(keyPairs, filepath.Clean(defaultConfigDir),
  145. "FTP portable")
  146. if err != nil {
  147. fmt.Printf("Unable to load FTPS key pair, cert file %q key file %q error: %v\n",
  148. portableFTPSCert, portableFTPSKey, err)
  149. os.Exit(1)
  150. }
  151. }
  152. if portableWebDAVPort >= 0 && portableWebDAVCert != "" && portableWebDAVKey != "" {
  153. keyPairs := []common.TLSKeyPair{
  154. {
  155. Cert: portableWebDAVCert,
  156. Key: portableWebDAVKey,
  157. ID: common.DefaultTLSKeyPaidID,
  158. },
  159. }
  160. _, err := common.NewCertManager(keyPairs, filepath.Clean(defaultConfigDir),
  161. "WebDAV portable")
  162. if err != nil {
  163. fmt.Printf("Unable to load WebDAV key pair, cert file %q key file %q error: %v\n",
  164. portableWebDAVCert, portableWebDAVKey, err)
  165. os.Exit(1)
  166. }
  167. }
  168. if portableHTTPPort >= 0 && portableHTTPSCert != "" && portableHTTPSKey != "" {
  169. keyPairs := []common.TLSKeyPair{
  170. {
  171. Cert: portableHTTPSCert,
  172. Key: portableHTTPSKey,
  173. ID: common.DefaultTLSKeyPaidID,
  174. },
  175. }
  176. _, err := common.NewCertManager(keyPairs, filepath.Clean(defaultConfigDir),
  177. "HTTP portable")
  178. if err != nil {
  179. fmt.Printf("Unable to load HTTPS key pair, cert file %q key file %q error: %v\n",
  180. portableHTTPSCert, portableHTTPSKey, err)
  181. os.Exit(1)
  182. }
  183. }
  184. pwd := portablePassword
  185. if portablePasswordFile != "" {
  186. content, err := os.ReadFile(portablePasswordFile)
  187. if err != nil {
  188. fmt.Printf("Unable to read password file %q: %v", portablePasswordFile, err)
  189. os.Exit(1)
  190. }
  191. pwd = strings.TrimSpace(string(content))
  192. }
  193. service.SetGraceTime(graceTime)
  194. service := service.Service{
  195. ConfigDir: util.CleanDirInput(configDir),
  196. ConfigFile: configFile,
  197. LogFilePath: portableLogFile,
  198. LogMaxSize: defaultLogMaxSize,
  199. LogMaxBackups: defaultLogMaxBackup,
  200. LogMaxAge: defaultLogMaxAge,
  201. LogCompress: defaultLogCompress,
  202. LogLevel: portableLogLevel,
  203. LogUTCTime: portableLogUTCTime,
  204. Shutdown: make(chan bool),
  205. PortableMode: 1,
  206. PortableUser: dataprovider.User{
  207. BaseUser: sdk.BaseUser{
  208. Username: portableUsername,
  209. Password: pwd,
  210. PublicKeys: portablePublicKeys,
  211. Permissions: permissions,
  212. HomeDir: portableDir,
  213. Status: 1,
  214. },
  215. Filters: dataprovider.UserFilters{
  216. BaseUserFilters: sdk.BaseUserFilters{
  217. FilePatterns: parsePatternsFilesFilters(),
  218. StartDirectory: portableStartDir,
  219. },
  220. },
  221. FsConfig: vfs.Filesystem{
  222. Provider: sdk.GetProviderByName(portableFsProvider),
  223. S3Config: vfs.S3FsConfig{
  224. BaseS3FsConfig: sdk.BaseS3FsConfig{
  225. Bucket: portableS3Bucket,
  226. Region: portableS3Region,
  227. AccessKey: portableS3AccessKey,
  228. RoleARN: portableS3RoleARN,
  229. Endpoint: portableS3Endpoint,
  230. StorageClass: portableS3StorageClass,
  231. ACL: portableS3ACL,
  232. KeyPrefix: portableS3KeyPrefix,
  233. UploadPartSize: int64(portableS3ULPartSize),
  234. UploadConcurrency: portableS3ULConcurrency,
  235. ForcePathStyle: portableS3ForcePathStyle,
  236. SkipTLSVerify: portableS3SkipTLSVerify,
  237. },
  238. AccessSecret: kms.NewPlainSecret(portableS3AccessSecret),
  239. },
  240. GCSConfig: vfs.GCSFsConfig{
  241. BaseGCSFsConfig: sdk.BaseGCSFsConfig{
  242. Bucket: portableGCSBucket,
  243. AutomaticCredentials: portableGCSAutoCredentials,
  244. StorageClass: portableGCSStorageClass,
  245. KeyPrefix: portableGCSKeyPrefix,
  246. },
  247. Credentials: kms.NewPlainSecret(portableGCSCredentials),
  248. },
  249. AzBlobConfig: vfs.AzBlobFsConfig{
  250. BaseAzBlobFsConfig: sdk.BaseAzBlobFsConfig{
  251. Container: portableAzContainer,
  252. AccountName: portableAzAccountName,
  253. Endpoint: portableAzEndpoint,
  254. AccessTier: portableAzAccessTier,
  255. KeyPrefix: portableAzKeyPrefix,
  256. UseEmulator: portableAzUseEmulator,
  257. UploadPartSize: int64(portableAzULPartSize),
  258. UploadConcurrency: portableAzULConcurrency,
  259. DownloadPartSize: int64(portableAzDLPartSize),
  260. DownloadConcurrency: portableAzDLConcurrency,
  261. },
  262. AccountKey: kms.NewPlainSecret(portableAzAccountKey),
  263. SASURL: kms.NewPlainSecret(portableAzSASURL),
  264. },
  265. CryptConfig: vfs.CryptFsConfig{
  266. Passphrase: kms.NewPlainSecret(portableCryptPassphrase),
  267. },
  268. SFTPConfig: vfs.SFTPFsConfig{
  269. BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
  270. Endpoint: portableSFTPEndpoint,
  271. Username: portableSFTPUsername,
  272. Fingerprints: portableSFTPFingerprints,
  273. Prefix: portableSFTPPrefix,
  274. DisableCouncurrentReads: portableSFTPDisableConcurrentReads,
  275. BufferSize: portableSFTPDBufferSize,
  276. },
  277. Password: kms.NewPlainSecret(portableSFTPPassword),
  278. PrivateKey: kms.NewPlainSecret(portableSFTPPrivateKey),
  279. },
  280. },
  281. },
  282. }
  283. err := service.StartPortableMode(portableSFTPDPort, portableFTPDPort, portableWebDAVPort, portableHTTPPort,
  284. portableSSHCommands, portableFTPSCert, portableFTPSKey, portableWebDAVCert, portableWebDAVKey,
  285. portableHTTPSCert, portableHTTPSKey)
  286. if err == nil {
  287. service.Wait()
  288. if service.Error == nil {
  289. os.Exit(0)
  290. }
  291. }
  292. os.Exit(1)
  293. },
  294. }
  295. )
  296. func init() {
  297. version.AddFeature("+portable")
  298. portableCmd.Flags().StringVarP(&directoryToServe, "directory", "d", ".", `Path to the directory to serve.
  299. This can be an absolute path or a path
  300. relative to the current directory
  301. `)
  302. portableCmd.Flags().StringVar(&portableStartDir, "start-directory", "/", `Alternate start directory.
  303. This is a virtual path not a filesystem
  304. path`)
  305. portableCmd.Flags().IntVarP(&portableSFTPDPort, "sftpd-port", "s", 0, `0 means a random unprivileged port,
  306. < 0 disabled`)
  307. portableCmd.Flags().IntVar(&portableFTPDPort, "ftpd-port", -1, `0 means a random unprivileged port,
  308. < 0 disabled`)
  309. portableCmd.Flags().IntVar(&portableWebDAVPort, "webdav-port", -1, `0 means a random unprivileged port,
  310. < 0 disabled`)
  311. portableCmd.Flags().IntVar(&portableHTTPPort, "httpd-port", -1, `0 means a random unprivileged port,
  312. < 0 disabled`)
  313. portableCmd.Flags().StringSliceVar(&portableSSHCommands, "ssh-commands", sftpd.GetDefaultSSHCommands(),
  314. `SSH commands to enable.
  315. "*" means any supported SSH command
  316. including scp
  317. `)
  318. portableCmd.Flags().StringVarP(&portableUsername, "username", "u", "", `Leave empty to use an auto generated
  319. value`)
  320. portableCmd.Flags().StringVarP(&portablePassword, "password", "p", "", `Leave empty to use an auto generated
  321. value`)
  322. portableCmd.Flags().StringVar(&portablePasswordFile, "password-file", "", `Read the password from the specified
  323. file path. Leave empty to use an auto
  324. generated value`)
  325. portableCmd.Flags().StringVarP(&portableLogFile, logFilePathFlag, "l", "", "Leave empty to disable logging")
  326. portableCmd.Flags().StringVar(&portableLogLevel, logLevelFlag, defaultLogLevel, `Set the log level.
  327. Supported values:
  328. debug, info, warn, error.
  329. `)
  330. portableCmd.Flags().BoolVar(&portableLogUTCTime, logUTCTimeFlag, false, "Use UTC time for logging")
  331. portableCmd.Flags().StringSliceVarP(&portablePublicKeys, "public-key", "k", []string{}, "")
  332. portableCmd.Flags().StringSliceVarP(&portablePermissions, "permissions", "g", []string{"list", "download"},
  333. `User's permissions. "*" means any
  334. permission`)
  335. portableCmd.Flags().StringArrayVar(&portableAllowedPatterns, "allowed-patterns", []string{},
  336. `Allowed file patterns case insensitive.
  337. The format is:
  338. /dir::pattern1,pattern2.
  339. For example: "/somedir::*.jpg,a*b?.png"`)
  340. portableCmd.Flags().StringArrayVar(&portableDeniedPatterns, "denied-patterns", []string{},
  341. `Denied file patterns case insensitive.
  342. The format is:
  343. /dir::pattern1,pattern2.
  344. For example: "/somedir::*.jpg,a*b?.png"`)
  345. portableCmd.Flags().StringVarP(&portableFsProvider, "fs-provider", "f", "osfs", `osfs => local filesystem (legacy value: 0)
  346. s3fs => AWS S3 compatible (legacy: 1)
  347. gcsfs => Google Cloud Storage (legacy: 2)
  348. azblobfs => Azure Blob Storage (legacy: 3)
  349. cryptfs => Encrypted local filesystem (legacy: 4)
  350. sftpfs => SFTP (legacy: 5)`)
  351. portableCmd.Flags().StringVar(&portableS3Bucket, "s3-bucket", "", "")
  352. portableCmd.Flags().StringVar(&portableS3Region, "s3-region", "", "")
  353. portableCmd.Flags().StringVar(&portableS3AccessKey, "s3-access-key", "", "")
  354. portableCmd.Flags().StringVar(&portableS3AccessSecret, "s3-access-secret", "", "")
  355. portableCmd.Flags().StringVar(&portableS3RoleARN, "s3-role-arn", "", "")
  356. portableCmd.Flags().StringVar(&portableS3Endpoint, "s3-endpoint", "", "")
  357. portableCmd.Flags().StringVar(&portableS3StorageClass, "s3-storage-class", "", "")
  358. portableCmd.Flags().StringVar(&portableS3ACL, "s3-acl", "", "")
  359. portableCmd.Flags().StringVar(&portableS3KeyPrefix, "s3-key-prefix", "", `Allows to restrict access to the
  360. virtual folder identified by this
  361. prefix and its contents`)
  362. portableCmd.Flags().IntVar(&portableS3ULPartSize, "s3-upload-part-size", 5, `The buffer size for multipart uploads
  363. (MB)`)
  364. portableCmd.Flags().IntVar(&portableS3ULConcurrency, "s3-upload-concurrency", 2, `How many parts are uploaded in
  365. parallel`)
  366. portableCmd.Flags().BoolVar(&portableS3ForcePathStyle, "s3-force-path-style", false, `Force path style bucket URL`)
  367. portableCmd.Flags().BoolVar(&portableS3SkipTLSVerify, "s3-skip-tls-verify", false, `If enabled the S3 client accepts any TLS
  368. certificate presented by the server and
  369. any host name in that certificate.
  370. In this mode, TLS is susceptible to
  371. man-in-the-middle attacks.
  372. This should be used only for testing.
  373. `)
  374. portableCmd.Flags().StringVar(&portableGCSBucket, "gcs-bucket", "", "")
  375. portableCmd.Flags().StringVar(&portableGCSStorageClass, "gcs-storage-class", "", "")
  376. portableCmd.Flags().StringVar(&portableGCSKeyPrefix, "gcs-key-prefix", "", `Allows to restrict access to the
  377. virtual folder identified by this
  378. prefix and its contents`)
  379. portableCmd.Flags().StringVar(&portableGCSCredentialsFile, "gcs-credentials-file", "", `Google Cloud Storage JSON credentials
  380. file`)
  381. portableCmd.Flags().IntVar(&portableGCSAutoCredentials, "gcs-automatic-credentials", 1, `0 means explicit credentials using
  382. a JSON credentials file, 1 automatic
  383. `)
  384. portableCmd.Flags().StringVar(&portableFTPSCert, "ftpd-cert", "", "Path to the certificate file for FTPS")
  385. portableCmd.Flags().StringVar(&portableFTPSKey, "ftpd-key", "", "Path to the key file for FTPS")
  386. portableCmd.Flags().StringVar(&portableWebDAVCert, "webdav-cert", "", `Path to the certificate file for WebDAV
  387. over HTTPS`)
  388. portableCmd.Flags().StringVar(&portableWebDAVKey, "webdav-key", "", `Path to the key file for WebDAV over
  389. HTTPS`)
  390. portableCmd.Flags().StringVar(&portableHTTPSCert, "httpd-cert", "", `Path to the certificate file for WebClient
  391. over HTTPS`)
  392. portableCmd.Flags().StringVar(&portableHTTPSKey, "httpd-key", "", `Path to the key file for WebClient over
  393. HTTPS`)
  394. portableCmd.Flags().StringVar(&portableAzContainer, "az-container", "", "")
  395. portableCmd.Flags().StringVar(&portableAzAccountName, "az-account-name", "", "")
  396. portableCmd.Flags().StringVar(&portableAzAccountKey, "az-account-key", "", "")
  397. portableCmd.Flags().StringVar(&portableAzSASURL, "az-sas-url", "", `Shared access signature URL`)
  398. portableCmd.Flags().StringVar(&portableAzEndpoint, "az-endpoint", "", `Leave empty to use the default:
  399. "blob.core.windows.net"`)
  400. portableCmd.Flags().StringVar(&portableAzAccessTier, "az-access-tier", "", `Leave empty to use the default
  401. container setting`)
  402. portableCmd.Flags().StringVar(&portableAzKeyPrefix, "az-key-prefix", "", `Allows to restrict access to the
  403. virtual folder identified by this
  404. prefix and its contents`)
  405. portableCmd.Flags().IntVar(&portableAzULPartSize, "az-upload-part-size", 5, `The buffer size for multipart uploads
  406. (MB)`)
  407. portableCmd.Flags().IntVar(&portableAzULConcurrency, "az-upload-concurrency", 5, `How many parts are uploaded in
  408. parallel`)
  409. portableCmd.Flags().IntVar(&portableAzDLPartSize, "az-download-part-size", 5, `The buffer size for multipart downloads
  410. (MB)`)
  411. portableCmd.Flags().IntVar(&portableAzDLConcurrency, "az-download-concurrency", 5, `How many parts are downloaded in
  412. parallel`)
  413. portableCmd.Flags().BoolVar(&portableAzUseEmulator, "az-use-emulator", false, "")
  414. portableCmd.Flags().StringVar(&portableCryptPassphrase, "crypto-passphrase", "", `Passphrase for encryption/decryption`)
  415. portableCmd.Flags().StringVar(&portableSFTPEndpoint, "sftp-endpoint", "", `SFTP endpoint as host:port for SFTP
  416. provider`)
  417. portableCmd.Flags().StringVar(&portableSFTPUsername, "sftp-username", "", `SFTP user for SFTP provider`)
  418. portableCmd.Flags().StringVar(&portableSFTPPassword, "sftp-password", "", `SFTP password for SFTP provider`)
  419. portableCmd.Flags().StringVar(&portableSFTPPrivateKeyPath, "sftp-key-path", "", `SFTP private key path for SFTP provider`)
  420. portableCmd.Flags().StringSliceVar(&portableSFTPFingerprints, "sftp-fingerprints", []string{}, `SFTP fingerprints to verify remote host
  421. key for SFTP provider`)
  422. portableCmd.Flags().StringVar(&portableSFTPPrefix, "sftp-prefix", "", `SFTP prefix allows restrict all
  423. operations to a given path within the
  424. remote SFTP server`)
  425. portableCmd.Flags().BoolVar(&portableSFTPDisableConcurrentReads, "sftp-disable-concurrent-reads", false, `Concurrent reads are safe to use and
  426. disabling them will degrade performance.
  427. Disable for read once servers`)
  428. portableCmd.Flags().Int64Var(&portableSFTPDBufferSize, "sftp-buffer-size", 0, `The size of the buffer (in MB) to use
  429. for transfers. By enabling buffering,
  430. the reads and writes, from/to the
  431. remote SFTP server, are split in
  432. multiple concurrent requests and this
  433. allows data to be transferred at a
  434. faster rate, over high latency networks,
  435. by overlapping round-trip times`)
  436. portableCmd.Flags().IntVar(&graceTime, graceTimeFlag, 0,
  437. `This grace time defines the number of
  438. seconds allowed for existing transfers
  439. to get completed before shutting down.
  440. A graceful shutdown is triggered by an
  441. interrupt signal.
  442. `)
  443. addConfigFlags(portableCmd)
  444. rootCmd.AddCommand(portableCmd)
  445. }
  446. func parsePatternsFilesFilters() []sdk.PatternsFilter {
  447. var patterns []sdk.PatternsFilter
  448. for _, val := range portableAllowedPatterns {
  449. p, exts := getPatternsFilterValues(strings.TrimSpace(val))
  450. if p != "" {
  451. patterns = append(patterns, sdk.PatternsFilter{
  452. Path: path.Clean(p),
  453. AllowedPatterns: exts,
  454. DeniedPatterns: []string{},
  455. })
  456. }
  457. }
  458. for _, val := range portableDeniedPatterns {
  459. p, exts := getPatternsFilterValues(strings.TrimSpace(val))
  460. if p != "" {
  461. found := false
  462. for index, e := range patterns {
  463. if path.Clean(e.Path) == path.Clean(p) {
  464. patterns[index].DeniedPatterns = append(patterns[index].DeniedPatterns, exts...)
  465. found = true
  466. break
  467. }
  468. }
  469. if !found {
  470. patterns = append(patterns, sdk.PatternsFilter{
  471. Path: path.Clean(p),
  472. AllowedPatterns: []string{},
  473. DeniedPatterns: exts,
  474. })
  475. }
  476. }
  477. }
  478. return patterns
  479. }
  480. func getPatternsFilterValues(value string) (string, []string) {
  481. if strings.Contains(value, "::") {
  482. dirExts := strings.Split(value, "::")
  483. if len(dirExts) > 1 {
  484. dir := strings.TrimSpace(dirExts[0])
  485. exts := []string{}
  486. for _, e := range strings.Split(dirExts[1], ",") {
  487. cleanedExt := strings.TrimSpace(e)
  488. if cleanedExt != "" {
  489. exts = append(exts, cleanedExt)
  490. }
  491. }
  492. if dir != "" && len(exts) > 0 {
  493. return dir, exts
  494. }
  495. }
  496. }
  497. return "", nil
  498. }
  499. func getFileContents(name string) (string, error) {
  500. fi, err := os.Stat(name)
  501. if err != nil {
  502. return "", err
  503. }
  504. if fi.Size() > 1048576 {
  505. return "", fmt.Errorf("%q is too big %v/1048576 bytes", name, fi.Size())
  506. }
  507. contents, err := os.ReadFile(name)
  508. if err != nil {
  509. return "", err
  510. }
  511. return string(contents), nil
  512. }