Skip to content

Commit 87f5cc7

Browse files
committed
Updated plugin generator to use SCI types
Signed-off-by: Eddie Knight <knight@linux.com>
1 parent 03d6eb0 commit 87f5cc7

File tree

3 files changed

+37
-141
lines changed

3 files changed

+37
-141
lines changed

cmd/generate-plugin.go

Lines changed: 34 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -3,89 +3,21 @@ package cmd
33
import (
44
"fmt"
55
"html/template"
6-
"net/http"
76
"os"
87
"path/filepath"
98
"strings"
109

1110
"github.com/go-git/go-git/v5"
1211
"github.com/spf13/cobra"
1312
"github.com/spf13/viper"
14-
"gopkg.in/yaml.v2"
15-
)
16-
17-
type ControlCatalog struct {
18-
CategoryIDFriendly string
19-
ServiceName string
20-
TestSuites map[string][]string
21-
22-
Metadata Metadata `yaml:"metadata"`
23-
24-
Controls []Control `yaml:"controls"`
25-
Features []Feature `yaml:"features"`
26-
Threats []Threat `yaml:"threats"`
27-
28-
LatestReleaseDetails ReleaseDetails `yaml:"latest_release_details"`
29-
}
30-
31-
// Metadata is a struct that represents the metadata.yaml file
32-
type Metadata struct {
33-
Title string `yaml:"title"`
34-
ID string `yaml:"id"`
35-
Description string `yaml:"description"`
36-
ReleaseDetails []ReleaseDetails `yaml:"release_details"`
37-
}
3813

39-
type ReleaseDetails struct {
40-
Version string `yaml:"version"`
41-
AssuranceLevel string `yaml:"assurance_level"`
42-
ThreatModelURL string `yaml:"threat_model_url"`
43-
ThreatModelAuthor string `yaml:"threat_model_author"`
44-
RedTeam string `yaml:"red_team"`
45-
RedTeamExerciseURL string `yaml:"red_team_exercise_url"`
46-
ReleaseManager ReleaseManager `yaml:"release_manager"`
47-
ChangeLog []string `yaml:"change_log"`
48-
}
49-
50-
type ReleaseManager struct {
51-
Name string `yaml:"name"`
52-
GithubId string `yaml:"github_id"`
53-
Company string `yaml:"company"`
54-
Summary string `yaml:"summary"`
55-
}
56-
57-
type Feature struct {
58-
ID string `yaml:"id"`
59-
Title string `yaml:"title"`
60-
Description string `yaml:"description"`
61-
}
62-
63-
type Threat struct {
64-
ID string `yaml:"id"`
65-
Title string `yaml:"title"`
66-
Description string `yaml:"description"`
67-
Features []string `yaml:"features"`
68-
MITRE []string `yaml:"mitre_attack"`
69-
}
70-
71-
type Control struct {
72-
IDFriendly string
73-
ID string `yaml:"id"`
74-
Title string `yaml:"title"`
75-
Objective string `yaml:"objective"`
76-
ControlFamily string `yaml:"control_family"`
77-
Threats []string `yaml:"threats"`
78-
NISTCSF string `yaml:"nist_csf"`
79-
MITREATTACK string `yaml:"mitre_attack"`
80-
ControlMappings map[string]interface{} `yaml:"control_mappings"`
81-
TestRequirements []TestRequirement `yaml:"test_requirements"`
82-
}
14+
"github.com/revanite-io/sci/pkg/layer2"
15+
)
8316

84-
type TestRequirement struct {
85-
IDFriendly string
86-
ID string `yaml:"id"`
87-
Text string `yaml:"text"`
88-
TLPLevels []string `yaml:"tlp_levels"`
17+
type CatalogData struct {
18+
layer2.Catalog
19+
ServiceName string
20+
TestSuites map[string][]string
8921
}
9022

9123
var TemplatesDir string
@@ -102,8 +34,6 @@ var genPluginCmd = &cobra.Command{
10234
}
10335

10436
func init() {
105-
rootCmd.AddCommand(genPluginCmd)
106-
10737
genPluginCmd.PersistentFlags().StringP("source-path", "p", "", "The source file to generate the plugin from.")
10838
genPluginCmd.PersistentFlags().StringP("local-templates", "", "", "Path to a directory to use instead of downloading the latest templates.")
10939
genPluginCmd.PersistentFlags().StringP("service-name", "n", "", "The name of the service (e.g. 'ECS, AKS, GCS').")
@@ -113,6 +43,8 @@ func init() {
11343
viper.BindPFlag("local-templates", genPluginCmd.PersistentFlags().Lookup("local-templates"))
11444
viper.BindPFlag("service-name", genPluginCmd.PersistentFlags().Lookup("service-name"))
11545
viper.BindPFlag("output-dir", genPluginCmd.PersistentFlags().Lookup("output-dir"))
46+
47+
rootCmd.AddCommand(genPluginCmd)
11648
}
11749

11850
func generatePlugin() {
@@ -189,8 +121,18 @@ func setupTemplatesDir() error {
189121
return err
190122
}
191123

192-
func generateFileFromTemplate(data ControlCatalog, templatePath, OutputDir string) error {
193-
tmpl, err := template.ParseFiles(templatePath)
124+
func generateFileFromTemplate(data CatalogData, templatePath, OutputDir string) error {
125+
templateContent, err := os.ReadFile(templatePath)
126+
if err != nil {
127+
return fmt.Errorf("error reading template file %s: %w", templatePath, err)
128+
}
129+
130+
tmpl, err := template.New("plugin").Funcs(template.FuncMap{
131+
"prettify": func(s string) string {
132+
return strings.TrimSpace(strings.ReplaceAll(
133+
strings.ReplaceAll(s, "\n", " "), ".", "_"))
134+
},
135+
}).Parse(string(templateContent))
194136
if err != nil {
195137
return fmt.Errorf("error parsing template file %s: %w", templatePath, err)
196138
}
@@ -201,14 +143,17 @@ func generateFileFromTemplate(data ControlCatalog, templatePath, OutputDir strin
201143
}
202144

203145
outputPath := filepath.Join(OutputDir, strings.TrimSuffix(relativePath, ".txt"))
146+
204147
err = os.MkdirAll(filepath.Dir(outputPath), os.ModePerm)
205148
if err != nil {
206149
return fmt.Errorf("error creating directories for %s: %w", outputPath, err)
207150
}
151+
208152
outputFile, err := os.Create(outputPath)
209153
if err != nil {
210154
return fmt.Errorf("error creating output file %s: %w", outputPath, err)
211155
}
156+
212157
defer outputFile.Close()
213158

214159
err = tmpl.Execute(outputFile, data)
@@ -219,76 +164,26 @@ func generateFileFromTemplate(data ControlCatalog, templatePath, OutputDir strin
219164
return nil
220165
}
221166

222-
func readData() (data ControlCatalog, err error) {
223-
if strings.HasPrefix(SourcePath, "http") {
224-
data, err = readYAMLURL()
225-
} else {
226-
data, err = readYAMLFile()
227-
}
167+
func readData() (data CatalogData, err error) {
168+
err = data.LoadControlFamiliesFile(SourcePath)
228169
if err != nil {
229170
return
230171
}
231172

232173
data.TestSuites = make(map[string][]string)
233-
data.CategoryIDFriendly = strings.ReplaceAll(data.Metadata.ID, ".", "_")
234174

235-
for i := range data.Controls {
236-
fmt.Println(data.Controls[i].ID)
237-
data.Controls[i].IDFriendly = strings.ReplaceAll(data.Controls[i].ID, ".", "_")
238-
// loop over objectives in test_requirements and replace newlines with empty string
239-
for j, testReq := range data.Controls[i].TestRequirements {
240-
// Some test requirements have newlines in them, which breaks the template
241-
data.Controls[i].TestRequirements[j].Text = strings.TrimSpace(strings.ReplaceAll(testReq.Text, "\n", " "))
242-
// Replace periods with underscores for the friendly ID
243-
data.Controls[i].TestRequirements[j].IDFriendly = strings.ReplaceAll(testReq.ID, ".", "_")
244-
245-
// Add the test ID to the TestSuites map for each TLP level
246-
for _, tlpLevel := range testReq.TLPLevels {
247-
if data.TestSuites[tlpLevel] == nil {
248-
data.TestSuites[tlpLevel] = []string{}
175+
for i, family := range data.ControlFamilies {
176+
for j := range family.Controls {
177+
for _, testReq := range data.ControlFamilies[i].Controls[j].Requirements {
178+
// Add the test ID to the TestSuites map for each TLP level
179+
for _, tlpLevel := range testReq.Applicability {
180+
if data.TestSuites[tlpLevel] == nil {
181+
data.TestSuites[tlpLevel] = []string{}
182+
}
183+
data.TestSuites[tlpLevel] = append(data.TestSuites[tlpLevel], testReq.ID)
249184
}
250-
data.TestSuites[tlpLevel] = append(data.TestSuites[tlpLevel], strings.ReplaceAll(testReq.ID, ".", "_"))
251185
}
252186
}
253187
}
254188
return
255189
}
256-
257-
func readYAMLURL() (data ControlCatalog, err error) {
258-
resp, err := http.Get(SourcePath)
259-
if err != nil {
260-
logger.Error("Failed to fetch URL: %v", err)
261-
return
262-
}
263-
defer resp.Body.Close()
264-
265-
if resp.StatusCode != http.StatusOK {
266-
logger.Error("Failed to fetch URL: %v", resp.Status)
267-
return
268-
}
269-
270-
decoder := yaml.NewDecoder(resp.Body)
271-
err = decoder.Decode(&data)
272-
if err != nil {
273-
logger.Error("Failed to decode YAML from URL: %v", err)
274-
return
275-
}
276-
277-
return
278-
}
279-
280-
func readYAMLFile() (data ControlCatalog, err error) {
281-
yamlFile, err := os.ReadFile(SourcePath)
282-
if err != nil {
283-
logger.Error(fmt.Sprintf("Error reading local source file: %s (%v)", SourcePath, err))
284-
return
285-
}
286-
287-
err = yaml.Unmarshal(yamlFile, &data)
288-
if err != nil {
289-
logger.Error("Error unmarshalling YAML file: %v", err)
290-
return
291-
}
292-
293-
return
294-
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ require (
77
github.com/hashicorp/go-hclog v1.6.3
88
github.com/hashicorp/go-plugin v1.6.3
99
github.com/privateerproj/privateer-sdk v0.6.1
10+
github.com/revanite-io/sci/pkg/layer2 v0.0.0-20250223164144-5fad3b3dc290
1011
github.com/spf13/cobra v1.9.1
1112
github.com/spf13/viper v1.19.0
12-
gopkg.in/yaml.v2 v2.4.0
1313
)
1414

1515
require (

go.sum

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
100100
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
101101
github.com/privateerproj/privateer-sdk v0.6.1 h1:uXojv/E61ETiuAU9SWprJcINA4lgV3bXK7YF/53cd68=
102102
github.com/privateerproj/privateer-sdk v0.6.1/go.mod h1:2utlXAO2fAOFgXKLjn/t6UqkU/4MpEYy2hLxacUsIMs=
103+
github.com/revanite-io/sci/pkg/layer2 v0.0.0-20250223164144-5fad3b3dc290 h1:F6fpBIr4ACgJm6+MOav08LFvsHbH6Xub1a4JS4VgR34=
104+
github.com/revanite-io/sci/pkg/layer2 v0.0.0-20250223164144-5fad3b3dc290/go.mod h1:+Qqc7Q6ODGeeEqc7hNCEzJ5XtVpG+Lt6FqOamEx8MNU=
103105
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
104106
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
105107
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -200,7 +202,6 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
200202
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
201203
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
202204
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
203-
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
204205
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
205206
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
206207
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

0 commit comments

Comments
 (0)