From e45de7d236acea7dd7d7c8406a14577633a9776c Mon Sep 17 00:00:00 2001 From: Warren He Date: Tue, 7 Nov 2023 14:14:07 -0800 Subject: [PATCH] runtime/evm: prevent saving invaild NFT metadata JSON --- analyzer/runtime/evm/erc721.go | 39 ++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/analyzer/runtime/evm/erc721.go b/analyzer/runtime/evm/erc721.go index 96882f3d0..ffd965694 100644 --- a/analyzer/runtime/evm/erc721.go +++ b/analyzer/runtime/evm/erc721.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "math/big" - "strings" "time" ethCommon "github.com/ethereum/go-ethereum/common" @@ -108,20 +107,46 @@ func evmDownloadNFTERC721Metadata(ctx context.Context, logger *log.Logger, sourc ) return nil } + // 1. Read metadata into interface{} to allow any syntactically correct + // JSON value. + var metadataAny interface{} limitedReader := io.LimitReader(rc, MaxMetadataBytes) - var metadataBuilder strings.Builder - teeReader := io.TeeReader(limitedReader, &metadataBuilder) - var metadata ERC721AssetMetadata - if err = json.NewDecoder(teeReader).Decode(&metadata); err != nil { - logger.Info("error decoding token metadata", + if err = json.NewDecoder(limitedReader).Decode(&metadataAny); err != nil { + logger.Info("error decoding token metadata as any", "uri", nftData.MetadataURI, "err", err, ) + if err = rc.Close(); err != nil { + return fmt.Errorf("closing metadata reader: %w", err) + } + return nil } if err = rc.Close(); err != nil { return fmt.Errorf("closing metadata reader: %w", err) } - nftData.Metadata = common.Ptr(metadataBuilder.String()) + // 2. Re-serialize into JSON for database. This is to normalize the + // syntax, in case Go and our database have slightly different opinions + // on what's valid JSON. + metadataNormalJSONBuf, err := json.Marshal(metadataAny) + if err != nil { + logger.Info("error re-encoding token metadata", + "uri", nftData.MetadataURI, + "err", err, + ) + return nil + } + nftData.Metadata = common.Ptr(string(metadataNormalJSONBuf)) + // 3. Parse it into the ERC-721 asset metadata schema. There's no function + // to convert these map[string]interface{} things into structs, so + // we'll just parse it again. + var metadata ERC721AssetMetadata + if err = json.Unmarshal(metadataNormalJSONBuf, &metadata); err != nil { + logger.Info("error decoding token metadata as asset metadata", + "uri", nftData.MetadataURI, + "err", err, + ) + return nil + } nftData.Name = metadata.Name nftData.Description = metadata.Description nftData.Image = metadata.Image