[#135] Add fuzzing tests for handlers
Some checks failed
/ DCO (pull_request) Failing after 1m15s
/ Builds (1.21) (pull_request) Successful in 1m20s
/ Builds (1.22) (pull_request) Successful in 1m25s
/ Vulncheck (pull_request) Successful in 1m24s
/ Lint (pull_request) Successful in 2m38s
/ Tests (1.21) (pull_request) Successful in 1m35s
/ Tests (1.22) (pull_request) Successful in 1m34s

This commit is contained in:
Roman Ognev 2024-06-20 12:45:26 +03:00
parent 5ee09790f0
commit 4bc3159f26
6 changed files with 1419 additions and 39 deletions

View file

@ -30,6 +30,15 @@ PKG_VERSION ?= $(shell echo $(VERSION) | sed "s/^v//" | \
sed "s/-/~/")-${OS_RELEASE} sed "s/-/~/")-${OS_RELEASE}
.PHONY: debpackage debclean .PHONY: debpackage debclean
FUZZING_DIR = $(shell pwd)/tests/fuzzing/files
NGFUZZ_REPO = https://b.yadro.com/scm/sdl/ngfuzz.git
NGFUZZ_BRANCH = feature/go-118-fuzz-build
GO_118_FUZZ_BUILD_REPO = b.yadro.com/obj/go-118-fuzz-build
FUZZ_TIMEOUT ?= 30
FUZZ_FUNCTIONS ?= "all"
FUZZ_AUX ?= ""
# Make all binaries # Make all binaries
all: $(BINS) all: $(BINS)
$(BINS): $(DIRS) dep $(BINS): $(DIRS) dep
@ -78,6 +87,36 @@ cover:
@go test -v -race ./... -coverprofile=coverage.txt -covermode=atomic @go test -v -race ./... -coverprofile=coverage.txt -covermode=atomic
@go tool cover -html=coverage.txt -o coverage.html @go tool cover -html=coverage.txt -o coverage.html
CLANG := $(shell which clang-17 2>/dev/null)
# Run fuzzing tests
.PHONY: check-clang all
check-clang:
ifeq ($(CLANG),)
@echo "clang-17 is not installed. Please install it before proceeding - https://apt.llvm.org/llvm.sh "
@exit 1
endif
.PHONY: install-ngfuzz
install-ngfuzz:
ifeq (,$(wildcard $(FUZZING_DIR)/ngfuzz))
@rm -rf $(FUZZING_DIR)/ngfuzz
@git clone -b $(NGFUZZ_BRANCH) $(NGFUZZ_REPO) $(FUZZING_DIR)/ngfuzz
@cd $(FUZZING_DIR)/ngfuzz && make
endif
.PHONY: install-fuzzing-deps
install-fuzzing-deps: check-clang install-ngfuzz
ifeq (,$(wildcard $(FUZZING_DIR)/go-118-fuzz-build))
GOBIN=$(FUZZING_DIR) go install $(GO_118_FUZZ_BUILD_REPO)
endif
.PHONY: fuzz
fuzz: install-fuzzing-deps
cd $(FUZZING_DIR)/ngfuzz && \
./ngfuzz -fuzz $(FUZZ_FUNCTIONS) -rootdir ../../../.. -timeout $(FUZZ_TIMEOUT) $(FUZZ_AUX) && \
./ngfuzz -report
# Reformat code # Reformat code
fmt: fmt:
@echo "⇒ Processing gofmt check" @echo "⇒ Processing gofmt check"
@ -150,6 +189,7 @@ version:
clean: clean:
rm -rf vendor rm -rf vendor
rm -rf $(BINDIR) rm -rf $(BINDIR)
rm -rf $(FUZZING_DIR)
# Package for Debian # Package for Debian
debpackage: debpackage:

View file

@ -6,6 +6,7 @@
</p> </p>
--- ---
[![Report](https://goreportcard.com/badge/git.frostfs.info/TrueCloudLab/frostfs-http-gw)](https://goreportcard.com/report/git.frostfs.info/TrueCloudLab/frostfs-http-gw) [![Report](https://goreportcard.com/badge/git.frostfs.info/TrueCloudLab/frostfs-http-gw)](https://goreportcard.com/report/git.frostfs.info/TrueCloudLab/frostfs-http-gw)
![Release](https://img.shields.io/badge/dynamic/json.svg?label=release&url=https://git.frostfs.info/api/v1/repos/TrueCloudLab/frostfs-http-gw/releases&query=$[0].tag_name&color=orange) ![Release](https://img.shields.io/badge/dynamic/json.svg?label=release&url=https://git.frostfs.info/api/v1/repos/TrueCloudLab/frostfs-http-gw/releases&query=$[0].tag_name&color=orange)
![License](https://img.shields.io/badge/license-GPL--3.0-orange.svg) ![License](https://img.shields.io/badge/license-GPL--3.0-orange.svg)
@ -13,6 +14,7 @@
# FrostFS HTTP Gateway # FrostFS HTTP Gateway
FrostFS HTTP Gateway bridges FrostFS internal protocol and HTTP standard. FrostFS HTTP Gateway bridges FrostFS internal protocol and HTTP standard.
- you can download one file per request from the FrostFS Network - you can download one file per request from the FrostFS Network
- you can upload one file per request into the FrostFS Network - you can upload one file per request into the FrostFS Network
@ -56,11 +58,14 @@ you can get the IP address of the node in the output of `make hosts` command
These two commands are functionally equivalent, they run the gate with one These two commands are functionally equivalent, they run the gate with one
backend node (and otherwise default settings): backend node (and otherwise default settings):
``` ```
$ frostfs-http-gw -p 192.168.130.72:8080 $ frostfs-http-gw -p 192.168.130.72:8080
$ HTTP_GW_PEERS_0_ADDRESS=192.168.130.72:8080 frostfs-http-gw $ HTTP_GW_PEERS_0_ADDRESS=192.168.130.72:8080 frostfs-http-gw
``` ```
It's also possible to specify uri scheme (grpc or grpcs) when using `-p`: It's also possible to specify uri scheme (grpc or grpcs) when using `-p`:
``` ```
$ frostfs-http-gw -p grpc://192.168.130.72:8080 $ frostfs-http-gw -p grpc://192.168.130.72:8080
$ HTTP_GW_PEERS_0_ADDRESS=grpcs://192.168.130.72:8080 frostfs-http-gw $ HTTP_GW_PEERS_0_ADDRESS=grpcs://192.168.130.72:8080 frostfs-http-gw
@ -80,6 +85,7 @@ gateway spread requests equally among them (using weight 1 and priority 1 for ev
``` ```
$ frostfs-http-gw -p 192.168.130.72:8080 -p 192.168.130.71:8080 $ frostfs-http-gw -p 192.168.130.72:8080 -p 192.168.130.71:8080
``` ```
If you want some specific load distribution proportions, use weights and priorities: If you want some specific load distribution proportions, use weights and priorities:
``` ```
@ -88,17 +94,22 @@ $ HTTP_GW_PEERS_0_ADDRESS=192.168.130.71:8080 HTTP_GW_PEERS_0_WEIGHT=1 HTTP_GW_P
HTTP_GW_PEERS_2_ADDRESS=192.168.130.73:8080 HTTP_GW_PEERS_2_WEIGHT=1 HTTP_GW_PEERS_2_PRIORITY=2 \ HTTP_GW_PEERS_2_ADDRESS=192.168.130.73:8080 HTTP_GW_PEERS_2_WEIGHT=1 HTTP_GW_PEERS_2_PRIORITY=2 \
frostfs-http-gw frostfs-http-gw
``` ```
This command will make gateway use 192.168.130.71 while it is healthy. Otherwise, it will make the gateway use This command will make gateway use 192.168.130.71 while it is healthy. Otherwise, it will make the gateway use
192.168.130.72 for 90% of requests and 192.168.130.73 for remaining 10%. 192.168.130.72 for 90% of requests and 192.168.130.73 for remaining 10%.
### Keys ### Keys
You can provide a wallet via `--wallet` or `-w` flag. You can also specify the account address using `--address` You can provide a wallet via `--wallet` or `-w` flag. You can also specify the account address using `--address`
(if no address provided default one will be used). If wallet is used, you need to set `HTTP_GW_WALLET_PASSPHRASE` variable to decrypt the wallet. (if no address provided default one will be used). If wallet is used, you need to set `HTTP_GW_WALLET_PASSPHRASE` variable to decrypt the wallet.
If no wallet provided, the gateway autogenerates a key pair it will use for FrostFS requests. If no wallet provided, the gateway autogenerates a key pair it will use for FrostFS requests.
``` ```
$ frostfs-http-gw -p $FROSTFS_NODE -w $WALLET_PATH --address $ACCOUNT_ADDRESS $ frostfs-http-gw -p $FROSTFS_NODE -w $WALLET_PATH --address $ACCOUNT_ADDRESS
``` ```
Example: Example:
``` ```
$ frostfs-http-gw -p 192.168.130.72:8080 -w wallet.json --address NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP $ frostfs-http-gw -p 192.168.130.72:8080 -w wallet.json --address NfgHwwTi3wHAS8aFAN243C5vGbkYDpqLHP
``` ```
@ -164,18 +175,23 @@ All timing options accept values with suffixes, so "15s" is 15 seconds and
"2m" is 2 minutes. "2m" is 2 minutes.
### Zip streaming ### Zip streaming
The gateway supports downloading files by common prefix (like dir) in zip format. You can enable compression The gateway supports downloading files by common prefix (like dir) in zip format. You can enable compression
using config or `HTTP_GW_ZIP_COMPRESSION=true` environment variable. using config or `HTTP_GW_ZIP_COMPRESSION=true` environment variable.
### Logging ### Logging
You can specify logging level using variable: You can specify logging level using variable:
``` ```
HTTP_GW_LOGGER_LEVEL=debug HTTP_GW_LOGGER_LEVEL=debug
``` ```
### Yaml file ### Yaml file
Configuration file is optional and can be used instead of environment variables/other parameters. Configuration file is optional and can be used instead of environment variables/other parameters.
It can be specified with `--config` parameter: It can be specified with `--config` parameter:
``` ```
$ frostfs-http-gw --config your-config.yaml $ frostfs-http-gw --config your-config.yaml
``` ```
@ -256,14 +272,17 @@ $ curl http://localhost:8082/get_by_attribute/container-name/FileName/object-nam
#### Create a container #### Create a container
You can create a container via [frostfs-cli](https://git.frostfs.info/TrueCloudLab/frostfs-node/releases): You can create a container via [frostfs-cli](https://git.frostfs.info/TrueCloudLab/frostfs-node/releases):
``` ```
$ frostfs-cli -r $FROSTFS_NODE -w $WALLET container create --policy $POLICY --basic-acl $ACL $ frostfs-cli -r $FROSTFS_NODE -w $WALLET container create --policy $POLICY --basic-acl $ACL
``` ```
where `$WALLET` is a path to user wallet, where `$WALLET` is a path to user wallet,
`$ACL` -- hex encoded basic ACL value or keywords 'private, 'public-read', 'public-read-write' and `$ACL` -- hex encoded basic ACL value or keywords 'private, 'public-read', 'public-read-write' and
`$POLICY` -- QL-encoded or JSON-encoded placement policy or path to file with it `$POLICY` -- QL-encoded or JSON-encoded placement policy or path to file with it
For example: For example:
``` ```
$ frostfs-cli -r 192.168.130.72:8080 -w ./wallet.json container create --policy "REP 3" --basic-acl public --await $ frostfs-cli -r 192.168.130.72:8080 -w ./wallet.json container create --policy "REP 3" --basic-acl public --await
``` ```
@ -275,19 +294,21 @@ the file `wallets/wallet.key`.
#### Prepare a file in a container #### Prepare a file in a container
To create a file via [frostfs-cli](https://git.frostfs.info/TrueCloudLab/frostfs-node/releases), run a command below: To create a file via [frostfs-cli](https://git.frostfs.info/TrueCloudLab/frostfs-node/releases), run a command below:
``` ```
$ frostfs-cli -r $FROSTFS_NODE -k $KEY object put --file $FILENAME --cid $CID $ frostfs-cli -r $FROSTFS_NODE -k $KEY object put --file $FILENAME --cid $CID
``` ```
where where
`$KEY` -- the key, please read the information [above](#create-a-container), `$KEY` -- the key, please read the information [above](#create-a-container),
`$CID` -- container ID. `$CID` -- container ID.
For example: For example:
``` ```
$ frostfs-cli -r 192.168.130.72:8080 -w ./wallet.json object put --file cat.png --cid Dxhf4PNprrJHWWTG5RGLdfLkJiSQ3AQqit1MSnEPRkDZ --attributes img_type=cat,my_attr=cute $ frostfs-cli -r 192.168.130.72:8080 -w ./wallet.json object put --file cat.png --cid Dxhf4PNprrJHWWTG5RGLdfLkJiSQ3AQqit1MSnEPRkDZ --attributes img_type=cat,my_attr=cute
``` ```
### Downloading ### Downloading
#### Requests #### Requests
@ -313,6 +334,7 @@ $ wget http://localhost:8082/get/container-name/2m8PtaoricLouCn5zE8hAFr3gZEBDCZF
``` ```
##### By attributes ##### By attributes
There is also more complex interface provided for attribute-based downloads, There is also more complex interface provided for attribute-based downloads,
it's usually used to retrieve files by their names, but any other attribute it's usually used to retrieve files by their names, but any other attribute
can be used as well. The generic syntax for it looks like this: can be used as well. The generic syntax for it looks like this:
@ -336,7 +358,9 @@ Example for file name attribute:
``` ```
$ wget http://localhost:8082/get_by_attribute/88GdaZFTcYJn1dqiSECss8kKPmmun6d6BfvC4zhwfLYM/FileName/cat.jpeg $ wget http://localhost:8082/get_by_attribute/88GdaZFTcYJn1dqiSECss8kKPmmun6d6BfvC4zhwfLYM/FileName/cat.jpeg
``` ```
Or when the filename includes special symbols: Or when the filename includes special symbols:
``` ```
$ wget http://localhost:8082/get_by_attribute/88GdaZFTcYJn1dqiSECss8kKPmmun6d6BfvC4zhwfLYM/FileName/cat+jpeg # means 'cat jpeg' $ wget http://localhost:8082/get_by_attribute/88GdaZFTcYJn1dqiSECss8kKPmmun6d6BfvC4zhwfLYM/FileName/cat+jpeg # means 'cat jpeg'
$ wget http://localhost:8082/get_by_attribute/88GdaZFTcYJn1dqiSECss8kKPmmun6d6BfvC4zhwfLYM/FileName/cat%25jpeg # means 'cat%jpeg' $ wget http://localhost:8082/get_by_attribute/88GdaZFTcYJn1dqiSECss8kKPmmun6d6BfvC4zhwfLYM/FileName/cat%25jpeg # means 'cat%jpeg'
@ -349,6 +373,7 @@ $ wget http://localhost:8082/get_by_attribute/Dxhf4PNprrJHWWTG5RGLdfLkJiSQ3AQqit
``` ```
Or when the attribute includes special symbols: Or when the attribute includes special symbols:
``` ```
$ wget http://localhost:8082/get_by_attribute/Dxhf4PNprrJHWWTG5RGLdfLkJiSQ3AQqit1MSnEPRkDZ/Olo%2Blo/100500 # means Olo+lo $ wget http://localhost:8082/get_by_attribute/Dxhf4PNprrJHWWTG5RGLdfLkJiSQ3AQqit1MSnEPRkDZ/Olo%2Blo/100500 # means Olo+lo
``` ```
@ -362,7 +387,9 @@ $ wget http://localhost:8082/get/Dxhf4PNprrJHWWTG5RGLdfLkJiSQ3AQqit1MSnEPRkDZ/2m
``` ```
##### Zip ##### Zip
You can download some dir (files with the same prefix) in zip (it will be compressed if config contains appropriate param): You can download some dir (files with the same prefix) in zip (it will be compressed if config contains appropriate param):
``` ```
$ wget http://localhost:8082/zip/Dxhf4PNprrJHWWTG5RGLdfLkJiSQ3AQqit1MSnEPRkDZ/common/prefix $ wget http://localhost:8082/zip/Dxhf4PNprrJHWWTG5RGLdfLkJiSQ3AQqit1MSnEPRkDZ/common/prefix
``` ```
@ -374,11 +401,11 @@ otherwise they will not be in the zip archive. You can upload file with this att
$ curl -F 'file=@cat.jpeg;filename=cat.jpeg' -H 'X-Attribute-FilePath: common/prefix/cat.jpeg' http://localhost:8082/upload/Dxhf4PNprrJHWWTG5RGLdfLkJiSQ3AQqit1MSnEPRkDZ $ curl -F 'file=@cat.jpeg;filename=cat.jpeg' -H 'X-Attribute-FilePath: common/prefix/cat.jpeg' http://localhost:8082/upload/Dxhf4PNprrJHWWTG5RGLdfLkJiSQ3AQqit1MSnEPRkDZ
``` ```
#### Replies #### Replies
You get object contents in the reply body (if GET method was used), but at the same time you also get a You get object contents in the reply body (if GET method was used), but at the same time you also get a
set of reply headers generated using the following rules: set of reply headers generated using the following rules:
* `Content-Length` is set to the length of the object * `Content-Length` is set to the length of the object
* `Content-Type` is autodetected dynamically by gateway * `Content-Type` is autodetected dynamically by gateway
* `Content-Disposition` is `inline` for regular requests and `attachment` for * `Content-Disposition` is `inline` for regular requests and `attachment` for
@ -422,6 +449,7 @@ $ curl --no-buffer -F 'file=@pipe;filename=catvideo.mp4' http://localhost:8082/u
``` ```
You can also add some attributes to your file using the following rules: You can also add some attributes to your file using the following rules:
* all "X-Attribute-*" headers get converted to object attributes with * all "X-Attribute-*" headers get converted to object attributes with
"X-Attribute-" prefix stripped, that is if you add "X-Attribute-Ololo: "X-Attribute-" prefix stripped, that is if you add "X-Attribute-Ololo:
100500" header to your request the resulting object will get "Ololo: 100500" header to your request the resulting object will get "Ololo:
@ -439,9 +467,11 @@ You can also add some attributes to your file using the following rules:
provide `X-Attribute-Timestamp` header of its own provide `X-Attribute-Timestamp` header of its own
--- ---
**NOTE** **NOTE**
There are some reserved headers type of `X-Attribute-SYSTEM-*` (headers are arranged in descending order of priority): There are some reserved headers type of `X-Attribute-SYSTEM-*` (headers are arranged in descending order of priority):
1. `X-Attribute-System-Expiration-Epoch: 100` 1. `X-Attribute-System-Expiration-Epoch: 100`
2. `X-Attribute-System-Expiration-Duration: 24h30m` 2. `X-Attribute-System-Expiration-Duration: 24h30m`
3. `X-Attribute-System-Expiration-Timestamp: 1637574797` 3. `X-Attribute-System-Expiration-Timestamp: 1637574797`
@ -453,6 +483,7 @@ which transforms to `X-Attribute-System-Expiration-Epoch`. So you can provide ex
For successful uploads you get JSON data in reply body with a container and For successful uploads you get JSON data in reply body with a container and
object ID, like this: object ID, like this:
``` ```
{ {
"object_id": "9ANhbry2ryjJY1NZbcjryJMRXG5uGNKd73kD3V1sVFsX", "object_id": "9ANhbry2ryjJY1NZbcjryJMRXG5uGNKd73kD3V1sVFsX",
@ -474,6 +505,7 @@ to grant access.
FrostFS Bearer Token basically is a container owner-signed policy (refer to FrostFS FrostFS Bearer Token basically is a container owner-signed policy (refer to FrostFS
documentation for more details). There are two options to pass them to gateway: documentation for more details). There are two options to pass them to gateway:
* "Authorization" header with "Bearer" type and base64-encoded token in * "Authorization" header with "Bearer" type and base64-encoded token in
credentials field credentials field
* "Bearer" cookie with base64-encoded token contents * "Bearer" cookie with base64-encoded token contents
@ -486,6 +518,7 @@ the corresponding header to the upload request. Accessing policy protected data
works the same way. works the same way.
##### Example ##### Example
In order to generate a bearer token, you need to have wallet (which will be used to sign the token) In order to generate a bearer token, you need to have wallet (which will be used to sign the token)
1. Suppose you have a container with private policy for wallet key 1. Suppose you have a container with private policy for wallet key
@ -500,9 +533,9 @@ $ frostfs-cli ape-manager add -r <endpoint> --wallet <wallet> \
--chain-id <chainID> --chain-id <chainID>
``` ```
2. Form a Bearer token (10000 is lifetime expiration in epoch) to impersonate 2. Form a Bearer token (10000 is lifetime expiration in epoch) to impersonate
HTTP Gateway request as wallet signed request and save it to **bearer.json**: HTTP Gateway request as wallet signed request and save it to **bearer.json**:
``` ```
{ {
"body": { "body": {
@ -518,11 +551,13 @@ $ frostfs-cli ape-manager add -r <endpoint> --wallet <wallet> \
``` ```
3. Sign it with the wallet: 3. Sign it with the wallet:
``` ```
$ frostfs-cli util sign bearer-token --from bearer.json --to signed.json -w <wallet> $ frostfs-cli util sign bearer-token --from bearer.json --to signed.json -w <wallet>
``` ```
4. Encode to base64 to use in header: 4. Encode to base64 to use in header:
``` ```
$ base64 -w 0 signed.json $ base64 -w 0 signed.json
# output: Ck4KKgoECAIQBhIiCiCZGdlbN7DPGPMg9rsWqV+p2XdMzUqknRiexewSFp8kmBIbChk17MUri6OJ0X5ftsHzy7NERDNFB4C92PcaGgMIkE4SZgohAxpsb7vfAso1F0X6hrm6WpRS14WsT3/Ct1SMoqRsT89KEkEEGxKi8GjKSf52YqhppgaOTQHbUsL3jn7SHLqS3ndAQ7NtAATnmRHleZw2V2xRRSRBQdjDC05KK83LhdSax72Fsw== # output: Ck4KKgoECAIQBhIiCiCZGdlbN7DPGPMg9rsWqV+p2XdMzUqknRiexewSFp8kmBIbChk17MUri6OJ0X5ftsHzy7NERDNFB4C92PcaGgMIkE4SZgohAxpsb7vfAso1F0X6hrm6WpRS14WsT3/Ct1SMoqRsT89KEkEEGxKi8GjKSf52YqhppgaOTQHbUsL3jn7SHLqS3ndAQ7NtAATnmRHleZw2V2xRRSRBQdjDC05KK83LhdSax72Fsw==
@ -551,6 +586,7 @@ $ echo 'NhVtreTTCoqsMQV5Wp55fqnriiUCpEaKm3' | base58 --decode | base64
``` ```
Then specify this value in Bearer Token Json Then specify this value in Bearer Token Json
``` ```
{ {
"body": { "body": {
@ -572,6 +608,24 @@ If enabled, Prometheus metrics are available at `localhost:8084` endpoint
and Pprof at `localhost:8083/debug/pprof` by default. Host and port can be configured. and Pprof at `localhost:8083/debug/pprof` by default. Host and port can be configured.
See [configuration](./docs/gate-configuration.md). See [configuration](./docs/gate-configuration.md).
## Fuzzing
To run fuzzing tests use the following command:
```shell
$ make fuzz
```
This command will install dependencies for the fuzzing process and run existing fuzzing tests.
You can also use the following arguments:
```
FUZZ_TIMEOUT - time to run each fuzzing test (default 30)
FUZZ_FUNCTIONS - fuzzing tests that will be started (default "all")
FUZZ_AUX - additional parameters for the fuzzer (for example, "-debug")
````
## Credits ## Credits
Please see [CREDITS](CREDITS.md) for details. Please see [CREDITS](CREDITS.md) for details.

1
go.mod
View file

@ -17,6 +17,7 @@ require (
github.com/ssgreg/journald v1.0.0 github.com/ssgreg/journald v1.0.0
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
github.com/testcontainers/testcontainers-go v0.13.0 github.com/testcontainers/testcontainers-go v0.13.0
github.com/trailofbits/go-fuzz-utils v0.0.0-20230413173806-58c38daa3cb4
github.com/valyala/fasthttp v1.34.0 github.com/valyala/fasthttp v1.34.0
go.opentelemetry.io/otel v1.16.0 go.opentelemetry.io/otel v1.16.0
go.opentelemetry.io/otel/trace v1.16.0 go.opentelemetry.io/otel/trace v1.16.0

2
go.sum
View file

@ -820,6 +820,8 @@ github.com/testcontainers/testcontainers-go v0.13.0 h1:OUujSlEGsXVo/ykPVZk3KanBN
github.com/testcontainers/testcontainers-go v0.13.0/go.mod h1:z1abufU633Eb/FmSBTzV6ntZAC1eZBYPtaFsn4nPuDk= github.com/testcontainers/testcontainers-go v0.13.0/go.mod h1:z1abufU633Eb/FmSBTzV6ntZAC1eZBYPtaFsn4nPuDk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/trailofbits/go-fuzz-utils v0.0.0-20230413173806-58c38daa3cb4 h1:GpfJ7OdNjS7BFTVwNCUI9L4aCJOFRbr5fdHqjdhoYE8=
github.com/trailofbits/go-fuzz-utils v0.0.0-20230413173806-58c38daa3cb4/go.mod h1:f3jBhpWvuZmue0HZK52GzRHJOYHYSILs/c8+K2S/J+o=
github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=

View file

@ -0,0 +1,574 @@
//go:build gofuzz
// +build gofuzz
package handler
import (
"bytes"
"context"
"encoding/json"
syserrors "errors"
"io"
"mime/multipart"
"net/http"
"testing"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
go_fuzz_utils "github.com/trailofbits/go-fuzz-utils"
"github.com/valyala/fasthttp"
)
var (
fuzzSuccessExitCode = 0
fuzzFailExitCode = 1
)
func prepareStrings(tp *go_fuzz_utils.TypeProvider, count int) ([]string, error) {
array := make([]string, count)
var err error
for i := 0; i < count; i++ {
err = tp.Reset()
if err != nil {
return nil, err
}
array[i], err = tp.GetString()
if err != nil {
return nil, err
}
}
return array, nil
}
func prepareBools(tp *go_fuzz_utils.TypeProvider, count int) ([]bool, error) {
array := make([]bool, count)
var err error
for i := 0; i < count; i++ {
err = tp.Reset()
if err != nil {
return nil, err
}
array[i], err = tp.GetBool()
if err != nil {
return nil, err
}
}
return array, nil
}
func getRandomDeterministicPositiveIntInRange(tp *go_fuzz_utils.TypeProvider, max int) (int, error) {
count, err := tp.GetInt()
if err != nil {
return -1, err
}
count = count % max
if count < 0 {
count += max
}
return count, nil
}
func generateHeaders(tp *go_fuzz_utils.TypeProvider, r *fasthttp.Request, params []string) error {
count, err := tp.GetInt()
if err != nil {
return err
}
count = count % len(params)
if count < 0 {
count += len(params)
}
for i := 0; i < count; i++ {
position, err := tp.GetInt()
if err != nil {
return err
}
position = position % len(params)
if position < 0 {
position += len(params)
}
v, err := tp.GetString()
if err != nil {
return err
}
r.Header.Set(params[position], v)
}
return nil
}
func maybeFillRandom(tp *go_fuzz_utils.TypeProvider, initValue string) (error, string) {
rnd, err := tp.GetBool()
if err != nil {
return err, ""
}
if rnd == true {
initValue, err = tp.GetString()
if err != nil {
return err, ""
}
}
return nil, initValue
}
func upload(tp *go_fuzz_utils.TypeProvider) (error, context.Context, *handlerContext, cid.ID, *fasthttp.RequestCtx, string, string, string) {
hc, err := prepareHandlerContext()
if err != nil {
return err, nil, nil, cid.ID{}, nil, "", "", ""
}
aclList := []acl.Basic{
acl.Private,
acl.PrivateExtended,
acl.PublicRO,
acl.PublicROExtended,
acl.PublicRW,
acl.PublicRWExtended,
acl.PublicAppend,
acl.PublicAppendExtended,
}
pos, err := getRandomDeterministicPositiveIntInRange(tp, len(aclList))
if err != nil {
return err, nil, nil, cid.ID{}, nil, "", "", ""
}
acl := aclList[pos]
strings, err := prepareStrings(tp, 6)
if err != nil {
return err, nil, nil, cid.ID{}, nil, "", "", ""
}
bktName := strings[0]
objFileName := strings[1]
valAttr := strings[2]
keyAttr := strings[3]
if len(bktName) == 0 {
return syserrors.New("not enought buckets"), nil, nil, cid.ID{}, nil, "", "", ""
}
cnrID, cnr, err := hc.prepareContainer(bktName, acl)
if err != nil {
return err, nil, nil, cid.ID{}, nil, "", "", ""
}
hc.frostfs.SetContainer(cnrID, cnr)
ctx := context.Background()
ctx = middleware.SetNamespace(ctx, "")
r := new(fasthttp.RequestCtx)
utils.SetContextToRequest(ctx, r)
r.SetUserValue("cid", cnrID.EncodeToString())
attributes := map[string]string{
object.AttributeFileName: objFileName,
keyAttr: valAttr,
}
var buff bytes.Buffer
w := multipart.NewWriter(&buff)
fw, err := w.CreateFormFile("file", attributes[object.AttributeFileName])
if err != nil {
return err, nil, nil, cid.ID{}, nil, "", "", ""
}
content, err := tp.GetBytes()
if err != nil {
return err, nil, nil, cid.ID{}, nil, "", "", ""
}
if _, err = io.Copy(fw, bytes.NewReader(content)); err != nil {
return err, nil, nil, cid.ID{}, nil, "", "", ""
}
if err = w.Close(); err != nil {
return err, nil, nil, cid.ID{}, nil, "", "", ""
}
r.Request.SetBodyStream(&buff, buff.Len())
r.Request.Header.Set("Content-Type", w.FormDataContentType())
r.Request.Header.Set("X-Attribute-"+keyAttr, valAttr)
err = generateHeaders(tp, &r.Request, []string{"X-Attribute-", "X-Attribute-DupKey", "X-Attribute-MyAttribute", "X-Attribute-System-DupKey", "X-Attribute-System-Expiration-Epoch1", "X-Attribute-SYSTEM-Expiration-Epoch2", "X-Attribute-system-Expiration-Epoch3", "X-Attribute-User-Attribute", "X-Attribute-", "X-Attribute-FileName", "X-Attribute-FROSTFS", "X-Attribute-neofs", "X-Attribute-SYSTEM", "X-Attribute-System-Expiration-Duration", "X-Attribute-System-Expiration-Epoch", "X-Attribute-System-Expiration-RFC3339", "X-Attribute-System-Expiration-Timestamp", "X-Attribute-Timestamp", "X-Attribute-" + strings[4], "X-Attribute-System-" + strings[5]})
if err != nil {
return err, nil, nil, cid.ID{}, nil, "", "", ""
}
hc.Handler().Upload(r)
if r.Response.StatusCode() != http.StatusOK {
return syserrors.New("error on upload"), nil, nil, cid.ID{}, nil, "", "", ""
}
return nil, ctx, hc, cnrID, r, objFileName, keyAttr, valAttr
}
func InitFuzzUpload() {
}
func DoFuzzUpload(input []byte) int {
// FUZZER INIT
if len(input) < 100 {
return fuzzFailExitCode
}
tp, err := go_fuzz_utils.NewTypeProvider(input)
if err != nil {
return fuzzFailExitCode
}
err, _, _, _, _, _, _, _ = upload(tp)
if err != nil {
return fuzzFailExitCode
}
return fuzzSuccessExitCode
}
func FuzzUpload(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
DoFuzzUpload(data)
})
}
func downloadOrHead(tp *go_fuzz_utils.TypeProvider, ctx context.Context, hc *handlerContext, cnrID cid.ID, resp *fasthttp.RequestCtx, filename string) (error, *fasthttp.RequestCtx) {
var putRes putResponse
defer func() {
if r := recover(); r != nil {
panic(resp)
}
}()
data := resp.Response.Body()
err := json.Unmarshal(data, &putRes)
if err != nil {
return err, nil
}
obj := hc.frostfs.objects[putRes.ContainerID+"/"+putRes.ObjectID]
attr := object.NewAttribute()
attr.SetKey(object.AttributeFilePath)
err, filename = maybeFillRandom(tp, filename)
if err != nil {
return err, nil
}
attr.SetValue(filename)
obj.SetAttributes(append(obj.Attributes(), *attr)...)
r := new(fasthttp.RequestCtx)
utils.SetContextToRequest(ctx, r)
cid := cnrID.EncodeToString()
err, cid = maybeFillRandom(tp, cid)
if err != nil {
return err, nil
}
oid := putRes.ObjectID
err, oid = maybeFillRandom(tp, oid)
if err != nil {
return err, nil
}
r.SetUserValue("cid", cid)
r.SetUserValue("oid", oid)
rnd, err := tp.GetBool()
if err != nil {
return err, nil
}
if rnd == true {
r.SetUserValue("download", "true")
}
return nil, r
}
func InitFuzzGet() {
}
func DoFuzzGet(input []byte) int {
// FUZZER INIT
if len(input) < 100 {
return fuzzFailExitCode
}
tp, err := go_fuzz_utils.NewTypeProvider(input)
if err != nil {
return fuzzFailExitCode
}
err, ctx, hc, cnrID, resp, filename, _, _ := upload(tp)
if err != nil {
return fuzzFailExitCode
}
err, r := downloadOrHead(tp, ctx, hc, cnrID, resp, filename)
if err != nil {
return fuzzFailExitCode
}
hc.Handler().DownloadByAddressOrBucketName(r)
return fuzzSuccessExitCode
}
func FuzzGet(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
DoFuzzUpload(data)
})
}
func InitFuzzHead() {
}
func DoFuzzHead(input []byte) int {
// FUZZER INIT
if len(input) < 100 {
return fuzzFailExitCode
}
tp, err := go_fuzz_utils.NewTypeProvider(input)
if err != nil {
return fuzzFailExitCode
}
err, ctx, hc, cnrID, resp, filename, _, _ := upload(tp)
if err != nil {
return fuzzFailExitCode
}
err, r := downloadOrHead(tp, ctx, hc, cnrID, resp, filename)
if err != nil {
return fuzzFailExitCode
}
hc.Handler().HeadByAddressOrBucketName(r)
return fuzzSuccessExitCode
}
func FuzzHead(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
DoFuzzHead(data)
})
}
func InitFuzzDownloadByAttribute() {
}
func DoFuzzDownloadByAttribute(input []byte) int {
// FUZZER INIT
if len(input) < 100 {
return fuzzFailExitCode
}
tp, err := go_fuzz_utils.NewTypeProvider(input)
if err != nil {
return fuzzFailExitCode
}
err, ctx, hc, cnrID, _, _, attrKey, attrVal := upload(tp)
if err != nil {
return fuzzFailExitCode
}
cid := cnrID.EncodeToString()
err, cid = maybeFillRandom(tp, cid)
if err != nil {
return fuzzFailExitCode
}
err, attrKey = maybeFillRandom(tp, attrKey)
if err != nil {
return fuzzFailExitCode
}
err, attrVal = maybeFillRandom(tp, attrVal)
if err != nil {
return fuzzFailExitCode
}
r := new(fasthttp.RequestCtx)
utils.SetContextToRequest(ctx, r)
r.SetUserValue("cid", cid)
r.SetUserValue("attr_key", attrKey)
r.SetUserValue("attr_val", attrVal)
hc.Handler().DownloadByAttribute(r)
return fuzzSuccessExitCode
}
func FuzzDownloadByAttribute(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
DoFuzzDownloadByAttribute(data)
})
}
func InitFuzzHeadByAttribute() {
}
func DoFuzzHeadByAttribute(input []byte) int {
// FUZZER INIT
if len(input) < 100 {
return fuzzFailExitCode
}
tp, err := go_fuzz_utils.NewTypeProvider(input)
if err != nil {
return fuzzFailExitCode
}
err, ctx, hc, cnrID, _, _, attrKey, attrVal := upload(tp)
if err != nil {
return fuzzFailExitCode
}
cid := cnrID.EncodeToString()
err, cid = maybeFillRandom(tp, cid)
if err != nil {
return fuzzFailExitCode
}
err, attrKey = maybeFillRandom(tp, attrKey)
if err != nil {
return fuzzFailExitCode
}
err, attrVal = maybeFillRandom(tp, attrVal)
if err != nil {
return fuzzFailExitCode
}
r := new(fasthttp.RequestCtx)
utils.SetContextToRequest(ctx, r)
r.SetUserValue("cid", cid)
r.SetUserValue("attr_key", attrKey)
r.SetUserValue("attr_val", attrVal)
hc.Handler().HeadByAttribute(r)
return fuzzSuccessExitCode
}
func FuzzHeadByAttribute(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
DoFuzzHeadByAttribute(data)
})
}
func InitFuzzDownloadZipped() {
}
func DoFuzzDownloadZipped(input []byte) int {
// FUZZER INIT
if len(input) < 100 {
return fuzzFailExitCode
}
tp, err := go_fuzz_utils.NewTypeProvider(input)
if err != nil {
return fuzzFailExitCode
}
err, ctx, hc, cnrID, _, _, _, _ := upload(tp)
if err != nil {
return fuzzFailExitCode
}
cid := cnrID.EncodeToString()
err, cid = maybeFillRandom(tp, cid)
if err != nil {
return fuzzFailExitCode
}
prefix := ""
err, prefix = maybeFillRandom(tp, prefix)
if err != nil {
return fuzzFailExitCode
}
r := new(fasthttp.RequestCtx)
utils.SetContextToRequest(ctx, r)
r.SetUserValue("cid", cid)
r.SetUserValue("prefix", prefix)
hc.Handler().DownloadZipped(r)
return fuzzSuccessExitCode
}
func FuzzDownloadZipped(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
DoFuzzDownloadZipped(data)
})
}
func InitFuzzStoreBearerTokenAppCtx() {
}
func DoFuzzStoreBearerTokenAppCtx(input []byte) int {
// FUZZER INIT
if len(input) < 100 {
return fuzzFailExitCode
}
tp, err := go_fuzz_utils.NewTypeProvider(input)
if err != nil {
return fuzzFailExitCode
}
prefix := ""
err, prefix = maybeFillRandom(tp, prefix)
if err != nil {
return fuzzFailExitCode
}
ctx := context.Background()
ctx = middleware.SetNamespace(ctx, "")
r := new(fasthttp.RequestCtx)
utils.SetContextToRequest(ctx, r)
strings, err := prepareStrings(tp, 3)
rand, err := prepareBools(tp, 2)
if rand[0] == true {
r.Request.Header.Set(fasthttp.HeaderAuthorization, "Bearer"+strings[0])
} else if rand[1] == true {
r.Request.Header.SetCookie(fasthttp.HeaderAuthorization, "Bearer"+strings[1])
} else {
r.Request.Header.Set(fasthttp.HeaderAuthorization, "Bearer"+strings[0])
r.Request.Header.SetCookie(fasthttp.HeaderAuthorization, "Bearer"+strings[1])
}
tokens.StoreBearerTokenAppCtx(ctx, r)
return fuzzSuccessExitCode
}
func FuzzStoreBearerTokenAppCtx(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
DoFuzzStoreBearerTokenAppCtx(data)
})
}

709
tests/fuzzing/dict.txt Normal file
View file

@ -0,0 +1,709 @@
" "
"()<>@,;:\\\\\\"
"-"
"--"
"."
"/"
"\\"
"_"
".0."
":0"
"0"
"0.0.0.0:8080"
".0.address"
".0.priority"
".0.weight"
"100"
"101"
"102"
"103"
"1.2.7"
"1.3.0"
"1d"
"1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb"
"24h"
"-5h"
"abc"
"access denied"
"Access Denied"
"actual"
"add attribute to result object"
"added path /get_by_attribute/{cid}/{attr_key}/{attr_val:*}"
"added path /get/{cid}/{oid}"
"added path /upload/{cid}"
"added path /zip/{cid}/{prefix}"
"added storage peer"
".address"
"address"
"addresses to listen"
"add server"
"aio"
"aio container started"
"all nodes represent a secondary object"
"allocs"
"application/json; charset=UTF-8"
"application/zip"
"app_name"
"archive/zip"
"attachment"
"attachment; filename=\\"
"attr_key"
"attr_val"
"Authorization"
"Average request duration (in milliseconds) for specific method on node in pool"
"avg_request_duration"
"bad base64 cookie"
"bad base64 header"
"bad cookie, but good header"
"bad header and cookie"
"bad header, but good cookie"
"Bar"
"bare"
"Bearer "
"Bearer"
"block"
"both deprecated and new system attributes formats are used, please use only one"
"--boundary"
"--boundary--"
"boundary"
"bucket"
"bucket cid"
"bucket name"
"bucketname"
"bufio"
"build zap logger instance: %v"
"b.yadro.com/obj/go-118-fuzz-build/testing"
"bytes"
"cache.buckets.lifetime"
"cache.buckets.size"
"cannot load TLS key pair from certFile '%s' and keyFile '%s': %w"
"can't base64-decode bearer token"
"can't base64-decode bearer token: %w"
"can't gracefully shut down service, force stop"
"can't shut down service"
"can't unmarshal bearer token"
"can't unmarshal bearer token: %w"
"CERTIFICATE"
"cert.pem"
"cert provider: disabled"
"cid"
"close temporary multipart/form file"
"close zip writer"
"commandline"
"config"
"config-dir"
"config dir path"
"config paths"
"connect_timeout"
"console"
"constant_labels,omitempty"
"container_id"
"container not found %s"
"container resolver will be disabled because of resolvers 'resolver_order' is empty"
"Content-Disposition"
"content of file"
"content of file1"
"content of file2"
"Content-Transfer-Encoding"
"Content-Transfer-Encoding: quoted-printable"
"Content-Type"
"context"
"__context_bearer_token_key"
"cookie token unmarshal error"
"copy object payload to zip file: %v"
"could not detect Content-Type from payload"
"could not detect Content-Type from payload: "
"could not dial nns: %w"
"could not encode response"
"could not fetch and store bearer token"
"could not fetch and store bearer token: "
"could not fetch bearer token"
"could not get bucket"
"could not get bucket: "
"could not load FrostFS private key"
"could not parse client time"
"could not prepare expiration header"
"could not prepare expiration header: "
"could not prepare listener: %w"
"could not process headers"
"could not receive multipart/form"
"could not receive multipart/form: "
"could not receive object"
"could not search for objects"
"could not search for objects: "
"could not store file in frostfs"
"could not unescape attr_key: "
"could not unescape attr_val: "
"could not unescape prefix: "
"couldn't decrypt account: %w"
"couldn't find wallet account for %s"
"couldn't get epoch durations from network info: %w"
"couldn't get namespace from context"
"couldn't parse creation date"
"couldn't parse value %s of header %s"
"couldn't put bucket info into cache"
"couldn't read password"
"couldn't resolve container '%s' as '%s': %w"
"couldn't resolve container '%s': %w"
"create_session"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"current_errors"
"Current HTTP gateway state"
"custom"
"data"
"data to return"
"Date header"
"debug"
"/debug/pprof/"
"/debug/pprof/cmdline"
"/debug/pprof/profile"
"/debug/pprof/symbol"
"/debug/pprof/trace"
"default"
"Default environments:"
"delete_container"
"delete_object"
"dev"
"dGVzdAo="
"dns"
"download"
"duplicate address"
"duplicate keys error"
"duplicate system keys error"
"elapsed"
"empty"
"enable pprof"
"enable prometheus"
"encoding/base64"
"encoding/hex"
"encoding/json"
"encoding/pem"
"endpoint"
"endpoint_info"
"Enter password > "
"EpochDuration is empty"
"error_detail"
"errors"
"expected"
"EXPIRATION_DURATION"
"EXPIRATION_EPOCH"
"expiration epoch '%d' must be greater than current epoch '%d'"
"EXPIRATION_RFC3339"
"EXPIRATION_TIMESTAMP"
"failed to add object to archive"
"failed to add server"
"failed to create connection pool"
"failed to create resolver"
"failed to create tree pool"
"failed to dial connection pool"
"failed to dial tree pool"
"failed to initialize tracing"
"failed to reconnect server"
"failed to reload config"
"failed to reload config because it's missed"
"failed to reload server parameters"
"failed to shutdown tracing"
"failed to unescape query"
"failed to update cert (listener close: %v): %w"
"failed to update resolvers"
"failed to update tls certs: %w"
"file"
"; filename="
"filename"
"FileName"
"File to export http gateway metrics to."
"first-value"
"first_value"
"flag"
"flag 'out' must be provided to dump metrics description"
"flush zip writer: %v"
"fmt"
"--foo"
"Foo"
"--foobar"
"foo-bar"
"Foo-Bar"
"foo.txt"
"form"
"form-data"
"found empty bearer token"
"friendly"
"FrostFS"
"frostfs.buffer_max_size_for_put"
"frostfs.client_cut"
"FrostFS HTTP Gateway\\nVersion: %s\\nGoVersion: %s\\n"
"FrostFS HTTP Gateway %s\\n"
"frostfs-http-gw"
"frostfs_http_gw"
"FrostFS nodes"
"frostfs.tree_pool_max_attempts"
"frost-http-gw"
"gateway timeout"
"/get/"
"get"
"GET"
"get_balance"
"/get_by_attribute/"
"get by attribute "
"get by attribute"
"/get_by_attribute/{cid}/{attr_key}/{attr_val:*}"
"/get/{cid}/{oid:*}"
"get_container"
"get_container_eacl"
"get frostfs container '%s': %w"
"get FrostFS object: %v"
"get latest object version"
"get_object"
"get zip "
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/api"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/api/layer"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/frostfs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/frostfs/services"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/multipart"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/metrics"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree"
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing/grpc"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ns"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool/tree"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool/tree/service"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"git.frostfs.info/TrueCloudLab/zapjournald"
"github.com/bluele/gcache"
"github.com/fasthttp/router"
"github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/input"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/prometheus/client_model/go"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"github.com/ssgreg/journald"
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
"github.com/trailofbits/go-fuzz-utils"
"github.com/valyala/fasthttp"
"golang.org/x/exp/slices"
"golang.org/x/net/http2"
"GOMEMLIMIT"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/semconv/v1.17.0"
"go.opentelemetry.io/otel/trace"
"goroutine"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gRPC connection rebalance timer"
"gRPC connect timeout"
"gRPC individual message timeout"
"gRPC request timeout"
"h"
"h2"
"head"
"head by attribute"
"header token unmarshal error"
"head_object"
"health"
"heap"
"hello"
"help"
"host"
"http://"
"http.client_address"
"HTTP_GW"
"http://localhost:30333"
"http.path"
"http.query"
"HTTP Server endpoint health"
"https://localhost:%d"
"id"
"ignore part, empty filename"
"ignore part, empty form name"
"incorrect logger level configuration %s (%v), "
"init full object reading via connection pool"
"init full payload range reading via connection pool"
"init object search via connection pool"
"init payload range reading via connection pool"
"inline"
"invalid address"
"invalid cache entry type"
"invalid cache size, using default value"
"invalid duration"
"invalid duration negative"
"invalid filepath '%s'"
"invalid lifetime, using default value (in seconds)"
"invalid metric type"
"invalid rfc3339"
"invalid rfc3339 zero"
"invalid servers configuration: no known server found"
"invalid timestamp sec"
"invalid timestamp sec zero"
"invalid token"
"invalid-token"
"invalid tree node: %w"
"io"
"IsDeleteMarker"
"iterating over selected objects failed"
"journald"
"key"
"key duplication error: "
"key duplication error: %s"
"key.pem"
"less than 512b"
"list_container"
"listen_address"
"listen and serve"
"localhost"
"localhost:8080"
"localhost:8082"
"localhost:8083"
"localhost:8084"
"logger.destination"
"logger.level"
"log level won't be updated"
"math"
"math/big"
"method"
"Method Not Allowed"
"metrics"
"metrics are disabled"
"mime"
"mime/multipart"
"mime/quotedprintable"
"more than 512b"
"multipart: boundary is empty"
"multipart: expecting a new Part; got line %q"
"multipart: NextPart: %v"
"multipart: unexpected line in Next(): %q"
"mutex"
"MyAttribute"
"myFile"
"\\n"
"name"
"namespace"
"\\n--boundary"
"neofs-"
"Neofs-"
"__NEOFS__"
"NEOFS-"
"Neofs-Expiration-Epoch"
"__NEOFS__EXPIRATION_EPOCH"
"Neofs-Random-Attr"
"__NEOFS__RANDOM_ATTR"
"net"
"net/http"
"net/http/pprof"
"net/textproto"
"net/url"
"network_info"
"newFile.txt"
"new_value"
"nil context"
"nns"
"node"
"no healthy servers"
"no resolvers"
"normal"
"not found"
"Not found"
"Not Found"
"no wallet path specified, creating ephemeral key automatically for this run"
".ns"
"Number of errors on current connections that will be reset after the threshold"
"object body close error: %w"
"object deleted"
"object_id"
"object not found"
"objects not found"
"object was deleted"
"object wasn't found"
"oid"
"OID"
"oid1"
"oid2"
"ok for cookie"
"ok for header"
"old_value"
"one node of the object version"
"one node of the object version and one node of the secondary object"
"os"
"os/signal"
"out"
"overall_errors"
"overall_node_errors"
"overall_node_requests"
"p"
"parameter"
"parse expiration epoch '%s': %w"
"path"
"peers"
"Peers preset:"
"pool"
"pool_error_threshold"
"pool must not be nil for DNS resolver"
"pprof"
"Pprof"
"pprof.address"
"pprof.enabled"
"prefix"
".priority"
"priority"
"Prometheus"
"prometheus.address"
"prometheus.enabled"
"put_container"
"put_object"
"put with bearer token in cookie"
"put with bearer token in header"
"put with duplicate keys "
"query"
"quoted-printable"
"range_object"
"read container via connection pool"
"read full object payload"
"read network info via client"
"read network info via client: %w"
"read object header via connection pool"
"read object list failed"
"read object list failed: "
"read payload"
"read system DNS parameter of the FrostFS: %w"
"rebalance_timer"
"reconnecting server..."
"reconnect_interval"
"remote"
"REP 1"
"request"
"REQUEST"
"request_timeout"
"resolve_bucket.default_namespaces"
"resolve_bucket.namespace_header"
"resolve_order"
"resolver nns won't be used since rpc_endpoint isn't provided"
"resolver settings must not be nil for DNS resolver"
"resolver settings must not be nil for NNS resolver"
"\\r\\n"
"\\r\\n--"
"\\r\\n--boundary"
"root"
"root2"
"rpc address must not be empty for NNS resolver"
"rpc_endpoint"
"RSA PRIVATE KEY"
"runtime"
"runtime/debug"
"runtime.soft_memory_limit"
"s3-client-cut"
"save object via connection pool"
"second-value"
"second_value"
"server"
"server.0.address"
"server reconnected successfully"
"service"
"service couldn't start on configured port"
"service hasn't started since it's disabled"
"service is running"
"set_container_eacl"
"set container name resolve order"
"several nodes of different types and with different timestamp"
"show help"
"show version"
"shutting down service"
"shutting down web server"
"SIGHUP config reload completed"
"SIGHUP config reload started"
"simple get "
"simple put "
"soft runtime memory defined with GOMEMLIMIT environment variable, config value skipped"
"soft runtime memory limit value updated"
"some-attr"
"some-get-by-attr-value"
"some-get-value"
"Some txt content. Content-Type must be detected properly by detector."
"sort"
"%s_%s_[N]_ADDRESS = string\\n"
"%s_%s_[N]_WEIGHT = float\\n"
"%s_%s = %s\\n"
"%s/%s/%s/%s"
"starting application"
"starting server"
"state"
"stdout"
"strconv"
"stream_timeout"
"strings"
"%s: %v"
"%s: %v: %s"
"%s: %w"
"%s: %w: %s"
"sync"
"syscall"
"system"
"system-"
"System-"
"__SYSTEM__"
"SYSTEM-"
"SystemDNS"
"system DNS parameter not found or empty"
"System-Expiration-Epoch"
"__SYSTEM__EXPIRATION_EPOCH"
"__SYSTEM__EXPIRATION_EPOCH1"
"__SYSTEM__EXPIRATION_EPOCH2"
"__SYSTEM__EXPIRATION_EPOCH3"
"System-Random-Attr"
"__SYSTEM__RANDOM_ATTR"
"\\t"
"%T"
"tcp"
"testing"
"test namespaces "
"test_resolver"
"text/plain; charset=utf-8"
"threadcreate"
"time"
"timeout"
"tls cert"
"tls.cert_file"
"TLS certificate path"
"tls disabled"
"tls enabled"
"tls.enabled"
"tls key"
"tls.key_file"
"TLS key path"
"token"
"TOKEN"
"token is missing in the context"
"token without payload"
"token without the bearer prefix"
"Total number of errors for connection in pool"
"Total number of errors in pool"
"Total number of requests to specific node in pool"
"tracing config updated"
"tracing.enabled"
"tracing.endpoint"
"tracing.exporter"
"true"
"truecloudlab/frostfs-aio:"
"type"
"unicode"
"unicode/utf8"
"unknown resolver: %s"
"/upload/"
"/upload/{cid}"
"upload_header.use_default_timestamp"
"User-Attribute"
"user value"
"using credentials"
"usupported filters"
"v"
"val"
"valid duration"
"valid epoch"
"valid epoch, valid duration"
"valid epoch, valid rfc3339"
"valid epoch, valid timestamp milli"
"valid epoch, valid timestamp nano"
"valid epoch, valid timestamp sec"
"valid max uint 64"
"valid rfc3339"
"valid timestamp sec"
"value"
"value in config"
"value should be one of %v"
"value %s of header %s must be in the future"
"value %s of header %s must be positive"
"variable_labels,omitempty"
"version"
"version_info"
"Version of current FrostFS HTTP Gate instance"
"w"
"wallet"
"wallet.address"
"wallet.passphrase"
"wallet.path"
"web.max_request_body_size"
"web.read_buffer_size"
"web.read_timeout"
"web.stream_request_body"
"web.write_buffer_size"
"web.write_timeout"
".weight"
"weight"
"WRONG BASE64"
"wrong destination for logger: %s"
"wrong object id"
"%w: %s"
"X-Attribute-"
"X-Attribute-DupKey"
"X-Attribute-FileName"
"X-Attribute-FROSTFS"
"X-Attribute-MyAttribute"
"X-Attribute-neofs"
"X-Attribute-System-"
"X-Attribute-SYSTEM"
"X-Attribute-System-DupKey"
"X-Attribute-System-Expiration-Duration"
"X-Attribute-System-Expiration-Epoch"
"X-Attribute-System-Expiration-Epoch1"
"X-Attribute-SYSTEM-Expiration-Epoch2"
"X-Attribute-system-Expiration-Epoch3"
"X-Attribute-System-Expiration-RFC3339"
"X-Attribute-System-Expiration-Timestamp"
"X-Attribute-Timestamp"
"X-Attribute-User-Attribute"
"x-container-id"
"X-Container-Id"
"X-Frostfs-Namespace"
"x-object-id"
"X-Object-Id"
"X-Owner-Id"
".yaml"
"yaml"
".yml"
"/zip/"
"zip"
"/zip/{cid}/{prefix:*}"
"zip.compression"
"zip create header: %v"
"/zipfolder"
"/zipfolder/dir"
"zipfolder/dir/name1.txt"
"zipfolder/name2.txt"