github.com/kaituanwang/hyperledger@v2.0.1+incompatible/core/chaincode/platforms/node/platform.go (about) 1 /* 2 # Copyright IBM Corp. All Rights Reserved. 3 # 4 # SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package node 8 9 import ( 10 "archive/tar" 11 "bytes" 12 "compress/gzip" 13 "errors" 14 "fmt" 15 "net/url" 16 "os" 17 "path/filepath" 18 "regexp" 19 "strings" 20 21 pb "github.com/hyperledger/fabric-protos-go/peer" 22 "github.com/hyperledger/fabric/common/flogging" 23 "github.com/hyperledger/fabric/core/chaincode/platforms/util" 24 ) 25 26 var logger = flogging.MustGetLogger("chaincode.platform.node") 27 28 // Platform for chaincodes written in Go 29 type Platform struct{} 30 31 // Returns whether the given file or directory exists or not 32 func pathExists(path string) (bool, error) { 33 _, err := os.Stat(path) 34 if err == nil { 35 return true, nil 36 } 37 if os.IsNotExist(err) { 38 return false, nil 39 } 40 return true, err 41 } 42 43 // Name returns the name of this platform 44 func (p *Platform) Name() string { 45 return pb.ChaincodeSpec_NODE.String() 46 } 47 48 // ValidateSpec validates Go chaincodes 49 func (p *Platform) ValidatePath(rawPath string) error { 50 path, err := url.Parse(rawPath) 51 if err != nil || path == nil { 52 return fmt.Errorf("invalid path: %s", err) 53 } 54 55 //Treat empty scheme as a local filesystem path 56 if path.Scheme == "" { 57 pathToCheck, err := filepath.Abs(rawPath) 58 if err != nil { 59 return fmt.Errorf("error obtaining absolute path of the chaincode: %s", err) 60 } 61 62 exists, err := pathExists(pathToCheck) 63 if err != nil { 64 return fmt.Errorf("error validating chaincode path: %s", err) 65 } 66 if !exists { 67 return fmt.Errorf("path to chaincode does not exist: %s", rawPath) 68 } 69 } 70 return nil 71 } 72 73 func (p *Platform) ValidateCodePackage(code []byte) error { 74 // FAB-2122: Scan the provided tarball to ensure it only contains source-code under 75 // the src folder. 76 // 77 // It should be noted that we cannot catch every threat with these techniques. Therefore, 78 // the container itself needs to be the last line of defense and be configured to be 79 // resilient in enforcing constraints. However, we should still do our best to keep as much 80 // garbage out of the system as possible. 81 re := regexp.MustCompile(`^(/)?(src|META-INF)/.*`) 82 is := bytes.NewReader(code) 83 gr, err := gzip.NewReader(is) 84 if err != nil { 85 return fmt.Errorf("failure opening codepackage gzip stream: %s", err) 86 } 87 tr := tar.NewReader(gr) 88 89 var foundPackageJson = false 90 for { 91 header, err := tr.Next() 92 if err != nil { 93 // We only get here if there are no more entries to scan 94 break 95 } 96 97 // -------------------------------------------------------------------------------------- 98 // Check name for conforming path 99 // -------------------------------------------------------------------------------------- 100 if !re.MatchString(header.Name) { 101 return fmt.Errorf("illegal file detected in payload: \"%s\"", header.Name) 102 } 103 if header.Name == "src/package.json" { 104 foundPackageJson = true 105 } 106 // -------------------------------------------------------------------------------------- 107 // Check that file mode makes sense 108 // -------------------------------------------------------------------------------------- 109 // Acceptable flags: 110 // ISREG == 0100000 111 // -rw-rw-rw- == 0666 112 // 113 // Anything else is suspect in this context and will be rejected 114 // -------------------------------------------------------------------------------------- 115 if header.Mode&^0100666 != 0 { 116 return fmt.Errorf("illegal file mode detected for file %s: %o", header.Name, header.Mode) 117 } 118 } 119 if !foundPackageJson { 120 return fmt.Errorf("no package.json found at the root of the chaincode package") 121 } 122 123 return nil 124 } 125 126 // Generates a deployment payload by putting source files in src/$file entries in .tar.gz format 127 func (p *Platform) GetDeploymentPayload(path string) ([]byte, error) { 128 129 var err error 130 131 // -------------------------------------------------------------------------------------- 132 // Write out our tar package 133 // -------------------------------------------------------------------------------------- 134 payload := bytes.NewBuffer(nil) 135 gw := gzip.NewWriter(payload) 136 tw := tar.NewWriter(gw) 137 138 folder := path 139 if folder == "" { 140 return nil, errors.New("ChaincodeSpec's path cannot be empty") 141 } 142 143 // trim trailing slash if it exists 144 if folder[len(folder)-1] == '/' { 145 folder = folder[:len(folder)-1] 146 } 147 148 logger.Debugf("Packaging node.js project from path %s", folder) 149 150 if err = util.WriteFolderToTarPackage(tw, folder, []string{"node_modules"}, nil, nil); err != nil { 151 logger.Errorf("Error writing folder to tar package %s", err) 152 return nil, fmt.Errorf("Error writing Chaincode package contents: %s", err) 153 } 154 155 // Write the tar file out 156 if err := tw.Close(); err != nil { 157 return nil, fmt.Errorf("Error writing Chaincode package contents: %s", err) 158 } 159 160 tw.Close() 161 gw.Close() 162 163 return payload.Bytes(), nil 164 } 165 166 func (p *Platform) GenerateDockerfile() (string, error) { 167 var buf []string 168 169 buf = append(buf, "FROM "+util.GetDockerImageFromConfig("chaincode.node.runtime")) 170 buf = append(buf, "ADD binpackage.tar /usr/local/src") 171 172 dockerFileContents := strings.Join(buf, "\n") 173 174 return dockerFileContents, nil 175 } 176 177 var buildScript = ` 178 set -e 179 if [ -x /chaincode/build.sh ]; then 180 /chaincode/build.sh 181 else 182 cp -R /chaincode/input/src/. /chaincode/output && cd /chaincode/output && npm install --production 183 fi 184 ` 185 186 func (p *Platform) DockerBuildOptions(path string) (util.DockerBuildOptions, error) { 187 return util.DockerBuildOptions{ 188 Image: util.GetDockerImageFromConfig("chaincode.node.runtime"), 189 Cmd: buildScript, 190 }, nil 191 }