Skip to content

Commit 39c002c

Browse files
committed
feat: update plugin generator to use SDK v1.9.0
Signed-off-by: Eddie Knight <knight@linux.com>
1 parent ab5d075 commit 39c002c

File tree

4 files changed

+153
-59
lines changed

4 files changed

+153
-59
lines changed

cmd/generate-plugin.go

Lines changed: 145 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,47 @@
11
package cmd
22

33
import (
4+
"bytes"
5+
"errors"
46
"fmt"
57
"html/template"
8+
"io"
69
"os"
710
"path/filepath"
811
"strings"
912

1013
"github.com/go-git/go-git/v5"
1114
"github.com/spf13/cobra"
1215
"github.com/spf13/viper"
16+
"gopkg.in/yaml.v3"
1317

14-
"github.com/revanite-io/sci/pkg/layer2"
18+
"github.com/ossf/gemara/layer2"
19+
sdkutils "github.com/privateerproj/privateer-sdk/utils"
1520
)
1621

1722
type CatalogData struct {
1823
layer2.Catalog
19-
ServiceName string
20-
TestSuites map[string][]string
24+
ServiceName string
25+
Requirements []string
26+
ApplicabilityCategories []string
27+
StrippedName string
2128
}
2229

23-
var TemplatesDir string
24-
var SourcePath string
25-
var OutputDir string
26-
27-
// versionCmd represents the version command
28-
var genPluginCmd = &cobra.Command{
29-
Use: "generate-plugin",
30-
Short: "Generate a new plugin",
31-
Run: func(cmd *cobra.Command, args []string) {
32-
generatePlugin()
33-
},
34-
}
30+
var (
31+
TemplatesDir string
32+
SourcePath string
33+
OutputDir string
34+
ServiceName string
35+
36+
// versionCmd represents the version command
37+
genPluginCmd = &cobra.Command{
38+
Use: "generate-plugin",
39+
Short: "Generate a new plugin",
40+
Run: func(cmd *cobra.Command, args []string) {
41+
generatePlugin()
42+
},
43+
}
44+
)
3545

3646
func init() {
3747
genPluginCmd.PersistentFlags().StringP("source-path", "p", "", "The source file to generate the plugin from.")
@@ -53,14 +63,18 @@ func generatePlugin() {
5363
logger.Error(err.Error())
5464
return
5565
}
56-
data, err := readData()
66+
data := CatalogData{}
67+
data.ServiceName = ServiceName
68+
69+
err = data.LoadFile("file://" + SourcePath)
5770
if err != nil {
5871
logger.Error(err.Error())
5972
return
6073
}
61-
data.ServiceName = viper.GetString("service-name")
62-
if data.ServiceName == "" {
63-
logger.Error("--service-name is required to generate a plugin.")
74+
75+
err = data.getAssessmentRequirements()
76+
if err != nil {
77+
logger.Error(err.Error())
6478
return
6579
}
6680

@@ -83,6 +97,11 @@ func generatePlugin() {
8397
if err != nil {
8498
logger.Error("Error walking through templates directory: %s", err)
8599
}
100+
101+
err = writeCatalogFile(&data.Catalog)
102+
if err != nil {
103+
logger.Error("Failed to write catalog to file: %s", err)
104+
}
86105
}
87106

88107
func setupTemplatingEnvironment() error {
@@ -91,6 +110,11 @@ func setupTemplatingEnvironment() error {
91110
return fmt.Errorf("--source-path is required to generate a plugin from a control set from local file or URL")
92111
}
93112

113+
ServiceName = viper.GetString("service-name")
114+
if ServiceName == "" {
115+
return fmt.Errorf("--service-name is required to generate a plugin.")
116+
}
117+
94118
if viper.GetString("local-templates") != "" {
95119
TemplatesDir = viper.GetString("local-templates")
96120
} else {
@@ -130,26 +154,36 @@ func generateFileFromTemplate(data CatalogData, templatePath, OutputDir string)
130154
return fmt.Errorf("error reading template file %s: %w", templatePath, err)
131155
}
132156

157+
// Determine relative path from templates dir so we can preserve subdirs in output
158+
relativePath, err := filepath.Rel(TemplatesDir, templatePath)
159+
if err != nil {
160+
return fmt.Errorf("error calculating relative path for %s: %w", templatePath, err)
161+
}
162+
163+
// If the template is not a text template, copy it over as-is (preserve mode)
164+
if filepath.Ext(templatePath) != ".txt" {
165+
return copyNonTemplateFile(templatePath, filepath.Join(OutputDir, relativePath))
166+
}
167+
133168
tmpl, err := template.New("plugin").Funcs(template.FuncMap{
134-
"as_text": func(s string) template.HTML {
135-
s = strings.TrimSpace(strings.ReplaceAll(s, "\n", " "))
136-
return template.HTML(s)
169+
"as_text": func(in string) template.HTML {
170+
return template.HTML(
171+
strings.TrimSpace(
172+
strings.ReplaceAll(in, "\n", " ")))
137173
},
138-
"as_id": func(s string) string {
139-
return strings.TrimSpace(
140-
strings.ReplaceAll(
141-
strings.ReplaceAll(s, ".", "_"), "-", "_"))
174+
"default": func(in string, out string) string {
175+
if in != "" {
176+
return in
177+
}
178+
return out
142179
},
180+
"snake_case": snakeCase,
181+
"simplifiedName": simplifiedName,
143182
}).Parse(string(templateContent))
144183
if err != nil {
145184
return fmt.Errorf("error parsing template file %s: %w", templatePath, err)
146185
}
147186

148-
relativePath, err := filepath.Rel(TemplatesDir, templatePath)
149-
if err != nil {
150-
return err
151-
}
152-
153187
outputPath := filepath.Join(OutputDir, strings.TrimSuffix(relativePath, ".txt"))
154188

155189
err = os.MkdirAll(filepath.Dir(outputPath), os.ModePerm)
@@ -177,26 +211,89 @@ func generateFileFromTemplate(data CatalogData, templatePath, OutputDir string)
177211
return nil
178212
}
179213

180-
func readData() (data CatalogData, err error) {
181-
err = data.LoadControlFamiliesFile(SourcePath)
182-
if err != nil {
183-
return
184-
}
185-
186-
data.TestSuites = make(map[string][]string)
187-
188-
for i, family := range data.ControlFamilies {
189-
for j := range family.Controls {
190-
for _, testReq := range data.ControlFamilies[i].Controls[j].Requirements {
191-
// Add the test ID to the TestSuites map for each TLP level
192-
for _, tlpLevel := range testReq.Applicability {
193-
if data.TestSuites[tlpLevel] == nil {
194-
data.TestSuites[tlpLevel] = []string{}
214+
func (c *CatalogData) getAssessmentRequirements() error {
215+
for _, family := range c.ControlFamilies {
216+
for _, control := range family.Controls {
217+
for _, requirement := range control.AssessmentRequirements {
218+
c.Requirements = append(c.Requirements, requirement.Id)
219+
// Add applicability categories if unique
220+
for _, a := range requirement.Applicability {
221+
if !sdkutils.StringSliceContains(c.ApplicabilityCategories, a) {
222+
c.ApplicabilityCategories = append(c.ApplicabilityCategories, a)
195223
}
196-
data.TestSuites[tlpLevel] = append(data.TestSuites[tlpLevel], testReq.ID)
197224
}
198225
}
199226
}
200227
}
201-
return
228+
if len(c.Requirements) == 0 {
229+
return errors.New("No requirements retrieved from catalog")
230+
}
231+
return nil
232+
}
233+
234+
func writeCatalogFile(catalog *layer2.Catalog) error {
235+
var b bytes.Buffer
236+
yamlEncoder := yaml.NewEncoder(&b)
237+
yamlEncoder.SetIndent(2) // this is the line that sets the indentation
238+
err := yamlEncoder.Encode(catalog)
239+
if err != nil {
240+
return fmt.Errorf("error marshaling YAML: %w", err)
241+
}
242+
243+
dirPath := filepath.Join(OutputDir, "data", simplifiedName(catalog.Metadata.Id, catalog.Metadata.Version))
244+
filePath := filepath.Join(dirPath, "catalog.yaml")
245+
246+
err = os.MkdirAll(dirPath, os.ModePerm)
247+
if err != nil {
248+
return fmt.Errorf("error creating directories for %s: %w", filePath, err)
249+
}
250+
251+
if err := os.WriteFile(filePath, b.Bytes(), 0644); err != nil {
252+
return fmt.Errorf("error writing YAML file: %w", err)
253+
}
254+
255+
return nil
256+
}
257+
258+
func snakeCase(in string) string {
259+
return strings.TrimSpace(
260+
strings.ReplaceAll(
261+
strings.ReplaceAll(in, ".", "_"), "-", "_"))
262+
}
263+
264+
func simplifiedName(catalogId string, catalogVersion string) string {
265+
return fmt.Sprintf("%s_%s", snakeCase(catalogId), snakeCase(catalogVersion))
266+
}
267+
268+
func copyNonTemplateFile(templatePath, relativePath string) error {
269+
outputPath := filepath.Join(OutputDir, relativePath)
270+
if err := os.MkdirAll(filepath.Dir(outputPath), os.ModePerm); err != nil {
271+
return fmt.Errorf("error creating directories for %s: %w", outputPath, err)
272+
}
273+
274+
// Copy file contents
275+
srcFile, err := os.Open(templatePath)
276+
if err != nil {
277+
return fmt.Errorf("error opening source file %s: %w", templatePath, err)
278+
}
279+
defer srcFile.Close()
280+
281+
dstFile, err := os.Create(outputPath)
282+
if err != nil {
283+
return fmt.Errorf("error creating destination file %s: %w", outputPath, err)
284+
}
285+
defer func() {
286+
_ = dstFile.Close()
287+
}()
288+
289+
if _, err := io.Copy(dstFile, srcFile); err != nil {
290+
return fmt.Errorf("error copying file to %s: %w", outputPath, err)
291+
}
292+
293+
// Try to preserve file mode from source
294+
if fi, err := os.Stat(templatePath); err == nil {
295+
_ = os.Chmod(outputPath, fi.Mode())
296+
}
297+
298+
return nil
202299
}

go.mod

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ require (
88
github.com/go-git/go-git/v5 v5.16.3
99
github.com/hashicorp/go-hclog v1.6.3
1010
github.com/hashicorp/go-plugin v1.7.0
11-
github.com/privateerproj/privateer-sdk v1.6.0
12-
github.com/revanite-io/sci v0.1.8
11+
github.com/ossf/gemara v0.12.1
12+
github.com/privateerproj/privateer-sdk v1.9.0
1313
github.com/spf13/cobra v1.10.1
1414
github.com/spf13/viper v1.21.0
15+
gopkg.in/yaml.v3 v3.0.1
1516
)
1617

1718
require (
@@ -38,7 +39,6 @@ require (
3839
github.com/mattn/go-colorable v0.1.14 // indirect
3940
github.com/mattn/go-isatty v0.0.20 // indirect
4041
github.com/oklog/run v1.1.0 // indirect
41-
github.com/ossf/gemara v0.10.1 // indirect
4242
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
4343
github.com/pjbgf/sha1cd v0.3.2 // indirect
4444
github.com/sagikazarmark/locafero v0.11.0 // indirect
@@ -61,8 +61,7 @@ require (
6161
google.golang.org/grpc v1.70.0 // indirect
6262
google.golang.org/protobuf v1.36.6 // indirect
6363
gopkg.in/warnings.v0 v0.1.2 // indirect
64-
gopkg.in/yaml.v3 v3.0.1 // indirect
6564
)
6665

6766
// For SDK Development Only
68-
// replace github.com/privateerproj/privateer-sdk => ./privateer-sdk
67+
// replace github.com/privateerproj/privateer-sdk => ../privateer-sdk

go.sum

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
9494
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
9595
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
9696
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
97-
github.com/ossf/gemara v0.10.1 h1:rvM8s/dAqF0QkCtztwgx92o/hWukRdS4rzsTpRT9chY=
98-
github.com/ossf/gemara v0.10.1/go.mod h1:FRRem1gQ9m+c3QiBLN/PkL/RfzyNpF3aO7AWqZVzerg=
97+
github.com/ossf/gemara v0.12.1 h1:Cyiytndw3HnyrctXE/iV4OzZURwypie2lmI7bf1bLAs=
98+
github.com/ossf/gemara v0.12.1/go.mod h1:rY4YvaWvOSJthTE2jHudjwcCRIQ31Y7GpEc3pyJPIPM=
9999
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
100100
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
101101
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
@@ -105,10 +105,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
105105
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
106106
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
107107
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
108-
github.com/privateerproj/privateer-sdk v1.6.0 h1:lljDUiesQEhgSH/6ZX+LRu+DeJPj1wHBJUAj+A6PAbc=
109-
github.com/privateerproj/privateer-sdk v1.6.0/go.mod h1:jNQQqTxvEnQBvR/BuRrbxMt8wxe7fX6mOC7PBTYknVI=
110-
github.com/revanite-io/sci v0.1.8 h1:JmVHJu2TX42WlNEVtOufxmyCi8PYXEZmXfk2ClfYsnI=
111-
github.com/revanite-io/sci v0.1.8/go.mod h1:KNBMtb28TKYJ0aq6P0jX1XaIBYQdAziTvnI7uU2H+5Q=
108+
github.com/privateerproj/privateer-sdk v1.9.0 h1:nBsbMBPJKU0fay5Lj2VBd07+I0W6+tl658SV4eMQqo8=
109+
github.com/privateerproj/privateer-sdk v1.9.0/go.mod h1:ngK2WiDbMywbUqji2X24Bbs4GMjK2j5vrjqgl2thBlo=
112110
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
113111
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
114112
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=

0 commit comments

Comments
 (0)