diff --git a/go.mod b/go.mod index 70d804098..43674f2ac 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,6 @@ require ( github.com/pmezard/go-difflib v1.0.0 github.com/sanathkr/yaml v1.0.0 github.com/stretchr/testify v1.6.1 - github.com/thepauleh/goserverless v0.0.0-20220613122947-21ff7c8de24c github.com/urfave/cli/v2 v2.3.0 github.com/zclconf/go-cty v1.7.0 go.opencensus.io v0.22.0 @@ -50,7 +49,6 @@ require ( github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/aws/aws-sdk-go v1.33.0 // indirect - github.com/awslabs/goformation/v4 v4.19.5 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/blang/semver v3.5.1+incompatible // indirect diff --git a/go.sum b/go.sum index bdeb0fec7..2089a54a6 100644 --- a/go.sum +++ b/go.sum @@ -88,10 +88,6 @@ github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.30.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.33.0 h1:Bq5Y6VTLbfnJp1IV8EL/qUU5qO1DYHda/zis/sqevkY= github.com/aws/aws-sdk-go v1.33.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/awslabs/goformation/v3 v3.1.0/go.mod h1:hQ5RXo3GNm2laHWKizDzU5DsDy+yNcenSca2UxN0850= -github.com/awslabs/goformation/v4 v4.1.0/go.mod h1:MBDN7u1lMNDoehbFuO4uPvgwPeolTMA2TzX1yO6KlxI= -github.com/awslabs/goformation/v4 v4.19.5 h1:Y+Tzh01tWg8gf//AgGKUamaja7Wx9NPiJf1FpZu4/iU= -github.com/awslabs/goformation/v4 v4.19.5/go.mod h1:JoNpnVCBOUtEz9bFxc9sjy8uBUCLF5c4D1L7RhRTVM8= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= @@ -317,9 +313,7 @@ github.com/hashicorp/vault v0.10.4/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bA github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= @@ -441,14 +435,12 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ github.com/olekukonko/tablewriter v0.0.0-20180506121414-d4647c9c7a84/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo v1.5.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/gomega v1.2.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= @@ -526,8 +518,6 @@ github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c h1:iRD1Cq github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c/go.mod h1:wk2XFUg6egk4tSDNZtXeKfe2G6690UVyt163PuUxBZk= github.com/terraform-providers/terraform-provider-openstack v1.15.0 h1:adpjqej+F8BAX9dHmuPF47sUIkgifeqBu6p7iCsyj0Y= github.com/terraform-providers/terraform-provider-openstack v1.15.0/go.mod h1:2aQ6n/BtChAl1y2S60vebhyJyZXBsuAI5G4+lHrT1Ew= -github.com/thepauleh/goserverless v0.0.0-20220613122947-21ff7c8de24c h1:N/93zRCWwsI/2yG+yuV4eczGSksRlsTi0oDaYk9h8Io= -github.com/thepauleh/goserverless v0.0.0-20220613122947-21ff7c8de24c/go.mod h1:lafnh08bRvuRl/ovO1WZpECUWMrpshm2DP4xLcRZOYg= github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc= github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 h1:cMjKdf4PxEBN9K5HaD9UMW8gkTbM0kMzkTa9SJe0WNQ= @@ -617,7 +607,6 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= diff --git a/src/cloudformation/structure/cloudformation_block.go b/src/cloudformation/structure/cloudformation_block.go index 9a32571b0..e1e1ff06d 100644 --- a/src/cloudformation/structure/cloudformation_block.go +++ b/src/cloudformation/structure/cloudformation_block.go @@ -13,6 +13,10 @@ type CloudformationBlock struct { structure.Block } +func (b *CloudformationBlock) GetFramework() string { + return "Cloudformation" +} + func (b *CloudformationBlock) UpdateTags() { if !b.IsTaggable { return diff --git a/src/common/structure/sls_models.go b/src/common/structure/sls_models.go new file mode 100644 index 000000000..a0cce1b6b --- /dev/null +++ b/src/common/structure/sls_models.go @@ -0,0 +1,16 @@ +package structure + +import "github.com/bridgecrewio/goformation/v5/cloudformation" + +// Template definition - only important fields +type Template struct { + FrameworkVersion string `json:"frameworkVersion,omitempty"` + Functions map[string]Function `json:"functions,omitempty"` + Resources cloudformation.Template `json:"resources,omitempty"` +} + +// Function definition - only important fields +type Function struct { + Name string `json:"name,omitempty"` + Tags map[string]interface{} `json:"tags,omitempty"` +} diff --git a/src/common/yaml/yaml_block.go b/src/common/yaml/yaml_block.go index c48de5c9a..a750c7046 100644 --- a/src/common/yaml/yaml_block.go +++ b/src/common/yaml/yaml_block.go @@ -5,4 +5,5 @@ import "github.com/bridgecrewio/yor/src/common/structure" type IYamlBlock interface { structure.IBlock UpdateTags() + GetFramework() string } diff --git a/src/common/yaml/yaml_writer.go b/src/common/yaml/yaml_writer.go index ab8388c86..e7aebc001 100644 --- a/src/common/yaml/yaml_writer.go +++ b/src/common/yaml/yaml_writer.go @@ -4,7 +4,6 @@ import ( "fmt" "math" "os" - "path/filepath" "regexp" "sort" "strings" @@ -14,7 +13,6 @@ import ( "github.com/bridgecrewio/yor/src/common/tagging/tags" "github.com/bridgecrewio/yor/src/common/utils" "github.com/sanathkr/yaml" - "github.com/thepauleh/goserverless/serverless" ) const SingleIndent = " " @@ -26,7 +24,7 @@ func WriteYAMLFile(readFilePath string, blocks []structure.IBlock, writeFilePath if err != nil { return fmt.Errorf("failed to read file %s because %s", readFilePath, err) } - isCfn := !strings.Contains(filepath.Base(readFilePath), "serverless") + isCfn := blocks[0].(IYamlBlock).GetFramework() == "Cloudformation" originLines := utils.GetLinesFromBytes(originFileSrc) oldResourcesLineRange := computeResourcesLineRange(originLines, blocks, isCfn) @@ -220,16 +218,6 @@ func getYAMLLines(rawBlock interface{}, isCfn bool) []string { textLines = utils.GetLinesFromBytes(yamlBytes) - if !isCfn { - slsFunction := rawBlock.(serverless.Function) - if utils.AllNil(slsFunction.VPC.SecurityGroupIds, slsFunction.VPC.SubnetIds) { - textLines = removeLineByAttribute(textLines, "vpc:") - } - if utils.AllNil(slsFunction.Package.Include, slsFunction.Package.Artifact, slsFunction.Package.Exclude, - slsFunction.Package.ExcludeDevDependencies, slsFunction.Package.Individually) { - textLines = removeLineByAttribute(textLines, "package") - } - } return textLines } diff --git a/src/common/yaml/yaml_writer_test.go b/src/common/yaml/yaml_writer_test.go index 2d38ebd73..a83b7925c 100644 --- a/src/common/yaml/yaml_writer_test.go +++ b/src/common/yaml/yaml_writer_test.go @@ -3,6 +3,7 @@ package yaml import ( "os" "path/filepath" + "strings" "testing" "github.com/awslabs/goformation/v5/cloudformation/s3" @@ -11,9 +12,23 @@ import ( "github.com/bridgecrewio/yor/src/common/tagging/simple" "github.com/bridgecrewio/yor/src/common/tagging/tags" "github.com/stretchr/testify/assert" - "github.com/thepauleh/goserverless/serverless" ) +type CustomBlock struct { + structure.Block +} + +func (cb *CustomBlock) Init(_ string, _ interface{}) {} + +func (cb *CustomBlock) UpdateTags() {} + +func (cb *CustomBlock) GetFramework() string { + if strings.Contains(cb.Block.FilePath, "serverless") { + return "Serverless" + } + return "Cloudformation" +} + func TestServerlessWriting(t *testing.T) { t.Run("test SLS writing", func(t *testing.T) { directory := "../../../tests/serverless/resources/no_tags" @@ -29,37 +44,39 @@ func TestServerlessWriting(t *testing.T) { tagGroup.InitTagGroup("", []string{}, []string{}) relExpectedPath := directory + "/serverless_tagged.yml" slsBlocks := []structure.IBlock{ - &structure.Block{ - FilePath: readFilePath, - ExitingTags: nil, - NewTags: []tags.ITag{&tags.Tag{Key: "new_tag", Value: "new_value"}}, - RawBlock: serverless.Function{ - Handler: "myFunction.handler", - Name: "myFunction", - Tags: map[string]interface{}{ - "new_tag": "new_value", + &CustomBlock{ + Block: structure.Block{ + FilePath: readFilePath, + ExitingTags: nil, + NewTags: []tags.ITag{&tags.Tag{Key: "new_tag", Value: "new_value"}}, + RawBlock: structure.Function{ + Name: "myFunction", + Tags: map[string]interface{}{ + "new_tag": "new_value", + }, }, + IsTaggable: true, + TagsAttributeName: "tags", + Lines: structure.Lines{Start: 13, End: 15}, + TagLines: structure.Lines{Start: -1, End: -1}, }, - IsTaggable: true, - TagsAttributeName: "tags", - Lines: structure.Lines{Start: 13, End: 15}, - TagLines: structure.Lines{Start: -1, End: -1}, }, - &structure.Block{ - FilePath: readFilePath, - ExitingTags: nil, - NewTags: []tags.ITag{&tags.Tag{Key: "new_tag", Value: "new_value"}}, - RawBlock: serverless.Function{ - Handler: "myFunction2.handler", - Name: "myFunction2", - Tags: map[string]interface{}{ - "new_tag": "new_value", + &CustomBlock{ + Block: structure.Block{ + FilePath: readFilePath, + ExitingTags: nil, + NewTags: []tags.ITag{&tags.Tag{Key: "new_tag", Value: "new_value"}}, + RawBlock: structure.Function{ + Name: "myFunction2", + Tags: map[string]interface{}{ + "new_tag": "new_value", + }, }, + IsTaggable: true, + TagsAttributeName: "tags", + Lines: structure.Lines{Start: 16, End: 18}, + TagLines: structure.Lines{Start: -1, End: -1}, }, - IsTaggable: true, - TagsAttributeName: "tags", - Lines: structure.Lines{Start: 16, End: 18}, - TagLines: structure.Lines{Start: -1, End: -1}, }, } f, _ := os.CreateTemp(directory, "serverless.*.yaml") @@ -97,27 +114,29 @@ func TestCFNWriting(t *testing.T) { }, } blocks := []structure.IBlock{ - &structure.Block{ - FilePath: readFilePath, - ExitingTags: []tags.ITag{}, - NewTags: extraTags, - RawBlock: &s3.Bucket{ - AccessControl: "PublicRead", - AWSCloudFormationDeletionPolicy: "Retain", - WebsiteConfiguration: &s3.Bucket_WebsiteConfiguration{ - ErrorDocument: "error.html", - IndexDocument: "index.html", - }, - Tags: []s3tags.Tag{ - {Key: "new_tag", Value: "new_val"}, - {Key: "another_tag", Value: "another_val"}, + &CustomBlock{ + Block: structure.Block{ + FilePath: readFilePath, + ExitingTags: []tags.ITag{}, + NewTags: extraTags, + RawBlock: &s3.Bucket{ + AccessControl: "PublicRead", + AWSCloudFormationDeletionPolicy: "Retain", + WebsiteConfiguration: &s3.Bucket_WebsiteConfiguration{ + ErrorDocument: "error.html", + IndexDocument: "index.html", + }, + Tags: []s3tags.Tag{ + {Key: "new_tag", Value: "new_val"}, + {Key: "another_tag", Value: "another_val"}, + }, }, + IsTaggable: true, + TagsAttributeName: "Tags", + Lines: structure.Lines{Start: 2, End: 9}, + TagLines: structure.Lines{Start: -1, End: -1}, + Name: "S3Bucket", }, - IsTaggable: true, - TagsAttributeName: "Tags", - Lines: structure.Lines{Start: 2, End: 9}, - TagLines: structure.Lines{Start: -1, End: -1}, - Name: "S3Bucket", }, } f, _ := os.CreateTemp(directory, "base.*.template") diff --git a/src/serverless/structure/serverless_block.go b/src/serverless/structure/serverless_block.go index 22d5ddcba..6ac78373f 100644 --- a/src/serverless/structure/serverless_block.go +++ b/src/serverless/structure/serverless_block.go @@ -2,8 +2,6 @@ package structure import ( "github.com/bridgecrewio/yor/src/common/structure" - "github.com/thepauleh/goserverless/serverless" - "go.opencensus.io/tag" ) @@ -11,6 +9,10 @@ type ServerlessBlock struct { structure.Block } +func (b *ServerlessBlock) GetFramework() string { + return "Serverless" +} + func (b *ServerlessBlock) UpdateTags() { if !b.IsTaggable { return @@ -31,7 +33,7 @@ func (b *ServerlessBlock) UpdateTags() { for _, mergedTag := range slsMergedTags { slsMergedTagsValue[mergedTag.Key.Name()] = mergedTag.Value } - rawFunction := b.RawBlock.(serverless.Function) + rawFunction := b.RawBlock.(structure.Function) if rawFunction.Tags == nil { rawFunction.Tags = make(map[string]interface{}, len(slsMergedTags)) } diff --git a/src/serverless/structure/serverless_block_test.go b/src/serverless/structure/serverless_block_test.go index b8db372df..f4c96a587 100644 --- a/src/serverless/structure/serverless_block_test.go +++ b/src/serverless/structure/serverless_block_test.go @@ -8,7 +8,6 @@ import ( "github.com/bridgecrewio/yor/src/common/structure" "github.com/bridgecrewio/yor/src/common/tagging/tags" - "github.com/thepauleh/goserverless/serverless" "github.com/stretchr/testify/assert" ) @@ -67,7 +66,7 @@ func TestServerlessBlock_UpdateTags(t *testing.T) { Block: structure.Block{ ExitingTags: existingTags, NewTags: newTags, - RawBlock: resource[0].GetRawBlock().(serverless.Function), + RawBlock: resource[0].GetRawBlock().(structure.Function), IsTaggable: true, TagsAttributeName: "tags", Lines: structure.Lines{Start: 4, End: 14}, @@ -77,7 +76,7 @@ func TestServerlessBlock_UpdateTags(t *testing.T) { b.UpdateTags() - currentRawBlock := b.RawBlock.(serverless.Function) + currentRawBlock := b.RawBlock.(structure.Function) currentTags := currentRawBlock.Tags sort.Slice(expectedMergedTags, func(i, j int) bool { return expectedMergedTags[i].GetKey() > expectedMergedTags[j].GetKey() diff --git a/src/serverless/structure/serverless_parser.go b/src/serverless/structure/serverless_parser.go index 0238b9d08..fe9762398 100644 --- a/src/serverless/structure/serverless_parser.go +++ b/src/serverless/structure/serverless_parser.go @@ -1,11 +1,14 @@ package structure import ( + "encoding/json" "fmt" + "github.com/bridgecrewio/goformation/v5/intrinsics" "math" "os" "path/filepath" "strings" + "sync" "github.com/bridgecrewio/yor/src/common" "github.com/bridgecrewio/yor/src/common/logger" @@ -14,8 +17,6 @@ import ( "github.com/bridgecrewio/yor/src/common/types" "github.com/bridgecrewio/yor/src/common/utils" yamlUtils "github.com/bridgecrewio/yor/src/common/yaml" - "github.com/thepauleh/goserverless" - "github.com/thepauleh/goserverless/serverless" ) const FunctionTagsAttributeName = "tags" @@ -25,6 +26,8 @@ type ServerlessParser struct { YamlParser types.YamlParser } +var slsParseLock sync.Mutex + func (p *ServerlessParser) Name() string { return "Serverless" } @@ -45,8 +48,8 @@ func (p *ServerlessParser) GetSupportedFileExtensions() []string { return []string{common.YamlFileType.Extension, common.YmlFileType.Extension} } -func goserverlessParse(file string) (*serverless.Template, error) { - var template *serverless.Template +func serverlessParse(file string) (*structure.Template, error) { + var template *structure.Template var err error defer func() { if e := recover(); e != nil { @@ -54,12 +57,16 @@ func goserverlessParse(file string) (*serverless.Template, error) { err = fmt.Errorf("failed to parse sls file %v: %v", file, e) } }() - - template, err = goserverless.Open(file) + slsParseLock.Lock() + template, err = Open(file) + slsParseLock.Unlock() return template, err } -func (p *ServerlessParser) ValidFile(_ string) bool { +func (p *ServerlessParser) ValidFile(file string) bool { + if _, err := serverlessParse(file); err != nil { + return false + } return true } @@ -67,11 +74,11 @@ func (p *ServerlessParser) ParseFile(filePath string) ([]structure.IBlock, error parsedBlocks := make([]structure.IBlock, 0) fileFormat := utils.GetFileFormat(filePath) fileName := filepath.Base(filePath) - if !(fileName == fmt.Sprintf("serverless.%s", fileFormat)) { + if !(fileName == fmt.Sprintf("serverless.%s", fileFormat) || fileName == fmt.Sprintf("config.%s", fileFormat)) { return nil, nil } // #nosec G304 - file is from user - template, err := goserverlessParse(filePath) + template, err := serverlessParse(filePath) if err != nil || template == nil || template.Functions == nil { if err != nil { logger.Warning(fmt.Sprintf("There was an error processing the serverless template: %s", err)) @@ -193,3 +200,28 @@ func (p *ServerlessParser) getTagsLines(filePath string, resourceLinesRange *str } return tagsLines } + +// Open and parse a Serverless template from file. +// Works with YAML formatted templates. +func Open(filename string) (*structure.Template, error) { + // #nosec G304 + data, err := os.ReadFile(filename) + if err != nil { + return nil, err + } + + return openYaml(data) +} + +func openYaml(input []byte) (*structure.Template, error) { + intrinsified, err := intrinsics.ProcessYAML(input, nil) + if err != nil { + return nil, err + } + template := &structure.Template{} + if err := json.Unmarshal(intrinsified, template); err != nil { + return nil, err + } + + return template, nil +}