1
0
mirror of https://github.com/strongdm/comply synced 2025-12-15 10:43:47 +00:00

Compare commits

...

11 Commits

Author SHA1 Message Date
Craine Runton
351c47e381 Update renderer to simplify framework criteria satisfaction based on controls in place, in addition to existing policy and narrative 2020-09-17 16:37:02 -05:00
Craine Runton
8f5a63db53 Read framework files as .yaml instead of .yml because that is what the file extensions typically come in. 2020-09-17 10:39:14 -05:00
Craine Runton
e289d6d8f4 Add a model for a Control, which ou can use to document control statements, owners, governing policies, etc. 2020-09-17 10:38:11 -05:00
Craine Runton
a66764470c Refactor "Controls" to "Criterion" so we can add the "Program Controls" model in next 2020-09-15 16:21:16 -05:00
Craine Runton
84e439e7cc Refactor "Standards" to "Frameworks" 2020-09-15 14:52:22 -05:00
Justin McCarthy
dbe49a09b3 increment patch for release (via Makefile) 2020-09-14 13:45:15 -07:00
Justin McCarthy
73bebe0202 go.mod: go mod vendor
Addresses #94
2020-09-14 13:44:19 -07:00
Justin McCarthy
f5f7c08b73 increment minor for release (via Makefile) 2020-09-14 12:01:37 -07:00
Justin McCarthy
b2276f9e54 increment patch for release (via Makefile) 2020-09-14 11:54:47 -07:00
Justin McCarthy
f1b5bbeff9 manual version bump 2020-09-14 11:54:24 -07:00
Justin McCarthy
b7acb1eecf increment patch for release (via Makefile) 2020-09-14 11:43:46 -07:00
28 changed files with 507 additions and 214 deletions

View File

@@ -1 +1 @@
1.4.2
1.5.1

View File

@@ -0,0 +1,33 @@
# Controls
Controls explicitly state a specific action that the organization will take to enforce a Policy goal.
## Format
```
name: Access Control Procedures
family: Access Control
identifier: AC-2
governingPolicy:
- policyName: Access Onboarding and Termination
policyID: SDM-AOTP
policyClause: 1.1
owner: Director, Security & Compliance
published: 2020-01-01
targets:
TSC 2017:
- CC6.1
- CC6.2
- CC6.3
NIST 800-53:
- AC-1
revisions:
- date: Sep 1 2020
comment: Initial documentation of control
---
1. Develop, document, and disseminate to all employees:
1. Organizational access control policy that:
1. Addresses purpose, scope, roles, responsibilities, management commitment, coordination among organizational entities, and compliance; and
```

View File

@@ -66,7 +66,7 @@ html lang=en
a onclick="javascript:show('procedures')" Procedures
li.top-nav.standards
strong
a onclick="javascript:show('standards')" Standards
a onclick="javascript:show('frameworks')" Frameworks
/ li.top-nav.evidence
/ a onclick="javascript:show('evidence')" Evidence Vault
#overview.section.top-nav.container.content
@@ -76,17 +76,17 @@ html lang=en
.columns.is-vcentered
.column.is-one-third
div
p.subtitle.is-3.has-text-centered Control Tracking
p.subtitle.is-3.has-text-centered CriterionTracking
.column.has-text-centered
div
p.heading Satisfied Controls
p.heading Satisfied Criteria
p.title
{{.Stats.ControlsSatisfied}}
{{.Stats.CriteriaSatisfied}}
.column.has-text-centered
div
p.heading Total Controls
p.title
{{.Stats.ControlsTotal}}
{{.Stats.CriteriaTotal}}
.columns.is-vcentered
.column.is-one-third
div
@@ -191,19 +191,19 @@ html lang=en
blockquote
h3
p
strong Standards
| specify the controls satisfied by the compliance program.
strong Frameworks
| specify the Framework Criteria satisfied by the compliance program.
table.table.is-size-4.is-fullwidth
thead
tr
th Control Key
th CriterionKey
th Name
th Satisfied?
th Satisfied By
tbody
{{range .Controls }}
{{range .Criteria}}
tr
td {{.ControlKey}}
td {{.criteriaKey}}
td
strong {{.Name}}
.subtitle {{.Description}}
@@ -234,4 +234,4 @@ html lang=en
show(destination)
}
}
}
}

1
go.mod
View File

@@ -33,6 +33,7 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/pkg/errors v0.9.1
github.com/robfig/cron v1.2.0
github.com/russross/blackfriday/v2 v2.0.1
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect
github.com/trivago/tgo v1.0.7 // indirect

9
go.sum
View File

@@ -124,13 +124,16 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
@@ -186,6 +189,7 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
@@ -234,9 +238,11 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@@ -271,6 +277,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
@@ -423,12 +430,14 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.5.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/russross/blackfriday v1.5.3-0.20200218234912-41c5fccfd6f6 h1:tlXG832s5pa9x9Gs3Rp2rTvEqjiDEuETUOSfBEiTcns=
github.com/russross/blackfriday v1.5.3-0.20200218234912-41c5fccfd6f6/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sanity-io/litter v1.3.0/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=

View File

@@ -12,7 +12,7 @@ import (
var todoCommand = cli.Command{
Name: "todo",
Usage: "list declared vs satisfied compliance controls",
Usage: "list declared vs satisfied compliance criteria",
Action: todoAction,
Before: projectMustExist,
}
@@ -24,42 +24,42 @@ func todoAction(c *cli.Context) error {
}
w := tablewriter.NewWriter(os.Stdout)
w.SetHeader([]string{"Standard", "Control", "Satisfied?", "Name"})
w.SetHeader([]string{"Framework", "Criterion", "Satisfied?", "Name"})
type row struct {
standard string
controlKey string
framework string
criterionKey string
satisfied string
controlName string
criterionName string
}
satisfied := model.ControlsSatisfied(d)
satisfied := model.CriteriaSatisfied(d)
var rows []row
for _, std := range d.Standards {
for id, c := range std.Controls {
for _, std := range d.Frameworks {
for id, c := range std.Criteria{
sat := "NO"
if _, ok := satisfied[id]; ok {
sat = color.GreenString("YES")
}
rows = append(rows, row{
standard: std.Name,
controlKey: id,
framework: std.Name,
criterionKey: id,
satisfied: sat,
controlName: c.Name,
criterionName: c.Name,
})
}
}
sort.Slice(rows, func(i, j int) bool {
return rows[i].controlKey < rows[j].controlKey
return rows[i].criterionKey < rows[j].criterionKey
})
w.SetAutoWrapText(false)
for _, r := range rows {
w.Append([]string{r.standard, r.controlKey, r.satisfied, r.controlName})
w.Append([]string{r.framework, r.criterionKey, r.satisfied, r.criterionName})
}
w.Render()

24
internal/model/control.go Normal file
View File

@@ -0,0 +1,24 @@
package model
import (
"time"
"html/template"
)
type Control struct {
Name string `yaml:"name"`
ID string `yaml:"identifier"`
Family string `yaml:"family"`
Owner string `yaml:"owner"`
GoverningPolicy []Policy `yaml:"governingPolicy"`
Revisions []Revision `yaml:"revisions"`
Targets Target `yaml:"targets"`
Published string `yaml:"published"`
FullPath string
OutputFilename string
ModifiedAt time.Time
Body string
BodyHTML template.HTML
}

View File

@@ -8,6 +8,7 @@ type Document struct {
Revisions []Revision `yaml:"majorRevisions"`
Satisfies Satisfaction `yaml:"satisfies"`
Targets Target `yaml:"targets"`
FullPath string
OutputFilename string
ModifiedAt time.Time

View File

@@ -1,18 +1,23 @@
package model
type Control struct {
type Criterion struct {
Family string `yaml:"family"`
Name string `yaml:"name"`
Description string `yaml:"description"`
Satisfied bool
SatisfiedBy []string
}
type Standard struct {
type Framework struct {
Name string `yaml:"name"`
Controls map[string]Control `yaml:",inline"`
Criteria map[string]Criterion `yaml:",inline"`
}
// ControlsSatisfied determines the unique controls currently satisfied by all Narratives, Policies, and Procedures
func ControlsSatisfied(data *Data) map[string][]string {
type Target map[string][]string
// CriteriaSatisfied determines the unique criteria currently satisfied by all Narratives, Policies, and Procedures
func CriteriaSatisfied(data *Data) map[string][]string {
satisfied := make(map[string][]string)
appendSatisfaction := func(in map[string][]string, k string, v string) []string {
@@ -25,22 +30,29 @@ func ControlsSatisfied(data *Data) map[string][]string {
}
for _, n := range data.Narratives {
for _, controlKeys := range n.Satisfies {
for _, key := range controlKeys {
for _, criteriaKeys := range n.Satisfies {
for _, key := range criteriaKeys {
satisfied[key] = appendSatisfaction(satisfied, key, n.OutputFilename)
}
}
}
for _, n := range data.Policies {
for _, controlKeys := range n.Satisfies {
for _, key := range controlKeys {
for _, criteriaKeys := range n.Satisfies {
for _, key := range criteriaKeys {
satisfied[key] = appendSatisfaction(satisfied, key, n.OutputFilename)
}
}
}
for _, n := range data.Controls {
for _, criteriaKeys := range n.Targets {
for _, key := range criteriaKeys {
satisfied[key] = appendSatisfaction(satisfied, key, n.OutputFilename)
}
}
}
for _, n := range data.Procedures {
for _, controlKeys := range n.Satisfies {
for _, key := range controlKeys {
for _, criteriaKeys := range n.Satisfies {
for _, key := range criteriaKeys {
satisfied[key] = appendSatisfaction(satisfied, key, n.OutputFilename)
}
}

View File

@@ -26,11 +26,15 @@ func ReadData() (*Data, error) {
if err != nil {
return nil, err
}
controls, err := ReadControls()
if err != nil {
return nil, err
}
procedures, err := ReadProcedures()
if err != nil {
return nil, err
}
standards, err := ReadStandards()
frameworks, err := ReadFrameworks()
if err != nil {
return nil, err
}
@@ -39,8 +43,9 @@ func ReadData() (*Data, error) {
Tickets: tickets,
Narratives: narratives,
Policies: policies,
Controls: controls,
Procedures: procedures,
Standards: standards,
Frameworks: frameworks,
}, nil
}
@@ -67,27 +72,27 @@ func tickets(rawTickets []string) ([]*Ticket, error) {
return tickets, nil
}
// ReadStandards loads standard definitions from the filesystem.
func ReadStandards() ([]*Standard, error) {
var standards []*Standard
// ReadFrameworks loads standard definitions from the filesystem.
func ReadFrameworks() ([]*Framework, error) {
var frameworks []*Framework
files, err := path.Standards()
files, err := path.Frameworks()
if err != nil {
return nil, errors.Wrap(err, "unable to enumerate paths")
}
for _, f := range files {
s := &Standard{}
s := &Framework{}
sBytes, err := ioutil.ReadFile(f.FullPath)
if err != nil {
return nil, errors.Wrap(err, "unable to read "+f.FullPath)
}
yaml.Unmarshal(sBytes, &s)
standards = append(standards, s)
frameworks = append(frameworks, s)
}
return standards, nil
return frameworks, nil
}
// ReadNarratives loads narrative descriptions from the filesystem.
@@ -166,6 +171,31 @@ func ReadPolicies() ([]*Document, error) {
return policies, nil
}
// ReadControls loads control documents from the filesystem
func ReadControls() ([]*Control, error) {
var controls []*Control
files, err := path.Controls()
if err != nil {
return nil, errors.Wrap(err, "unable to enumerate paths")
}
for _, f := range files {
c := &Control{}
mdmd := loadMDMD(f.FullPath)
err = yaml.Unmarshal([]byte(mdmd.yaml), &c)
if err != nil {
return nil, errors.Wrap(err, "unable to parse "+f.FullPath)
}
c.Body = mdmd.body
c.FullPath = f.FullPath
c.ModifiedAt = f.Info.ModTime()
c.OutputFilename = fmt.Sprintf("%s-%s.pdf", config.Config().FilePrefix, c.ID)
controls = append(controls, c)
}
return controls, nil
}
type metadataMarkdown struct {
yaml string
body string

View File

@@ -1,12 +1,13 @@
package model
type Data struct {
Standards []*Standard
Narratives []*Document
Policies []*Document
Procedures []*Procedure
Tickets []*Ticket
Audits []*Audit
Frameworks []*Framework
Narratives []*Document
Policies []*Document
Controls []*Control
Procedures []*Procedure
Tickets []*Ticket
Audits []*Audit
}
type Revision struct {

7
internal/model/policy.go Normal file
View File

@@ -0,0 +1,7 @@
package model
type Policy struct {
Name string `yaml:"policyName"`
ID string `yaml:"policyID"`
Clause string `yaml:"policyClause"`
}

View File

@@ -9,6 +9,7 @@ type Procedure struct {
Revisions []Revision `yaml:"majorRevisions"`
Satisfies Satisfaction `yaml:"satisfies"`
Targets Target `yaml:"targets"`
FullPath string
OutputFilename string
ModifiedAt time.Time

View File

@@ -15,9 +15,9 @@ type File struct {
Info os.FileInfo
}
// Standards lists all standard files.
func Standards() ([]File, error) {
return filesFor("standards", "yml")
// Frameworks lists all standard files.
func Frameworks() ([]File, error) {
return filesFor("frameworks", "yaml")
}
// Narratives lists all narrative files.
@@ -30,6 +30,11 @@ func Policies() ([]File, error) {
return filesFor("policies", "md")
}
// Controls lists all control files.
func Controls() ([]File, error) {
return filesFor("controls", "md")
}
// Procedures lists all procedure files.
func Procedures() ([]File, error) {
return filesFor("procedures", "md")

View File

@@ -2,12 +2,14 @@ package render
import (
"fmt"
"sort"
"time"
"html/template"
"github.com/pkg/errors"
"github.com/strongdm/comply/internal/config"
"github.com/strongdm/comply/internal/model"
"github.com/russross/blackfriday/v2"
)
type project struct {
@@ -16,8 +18,10 @@ type project struct {
}
type stats struct {
ControlsTotal int
ControlsSatisfied int
ControlsTotal int
CriteriaTotal int
CriteriaSatisfied int
ProcedureTotal int
ProcedureOpen int
@@ -30,21 +34,21 @@ type stats struct {
type renderData struct {
// duplicates Project.OrganizationName
Name string
Project *project
Stats *stats
Narratives []*model.Document
Policies []*model.Document
Procedures []*model.Procedure
Standards []*model.Standard
Tickets []*model.Ticket
Controls []*control
Links *model.TicketLinks
Name string
Project *project
Stats *stats
Narratives []*model.Document
Policies []*model.Document
Controls []*model.Control
Procedures []*model.Procedure
Frameworks []*model.Framework
Tickets []*model.Ticket
Links *model.TicketLinks
}
type control struct {
Standard string
ControlKey string
type criterion struct {
Framework string
CriteriaKey string
Name string
Description string
Satisfied bool
@@ -63,36 +67,27 @@ func load() (*model.Data, *renderData, error) {
Name: fmt.Sprintf("%s Compliance Program", cfg.Name),
}
satisfied := model.ControlsSatisfied(modelData)
controls := make([]*control, 0)
for _, standard := range modelData.Standards {
for key, c := range standard.Controls {
satisfied := model.CriteriaSatisfied(modelData)
for _, framework := range modelData.Frameworks {
for key, c := range framework.Criteria{
satisfactions, ok := satisfied[key]
satisfied := ok && len(satisfactions) > 0
controls = append(controls, &control{
Standard: standard.Name,
ControlKey: key,
Name: c.Name,
Description: c.Description,
Satisfied: satisfied,
SatisfiedBy: satisfactions,
})
c.Satisfied = satisfied
c.SatisfiedBy = satisfactions
framework.Criteria[key] = c
}
}
sort.Slice(controls, func(i, j int) bool {
return controls[i].ControlKey < controls[j].ControlKey
})
rd := &renderData{}
rd.Narratives = modelData.Narratives
rd.Policies = modelData.Policies
rd.Controls = modelData.Controls
rd.Procedures = modelData.Procedures
rd.Standards = modelData.Standards
rd.Frameworks = modelData.Frameworks
rd.Tickets = modelData.Tickets
rd.Links = &model.TicketLinks{}
rd.Project = project
rd.Name = project.OrganizationName
rd.Controls = controls
ts, err := config.Config().TicketSystem()
if err != nil {
@@ -114,6 +109,12 @@ func loadWithStats() (*model.Data, *renderData, error) {
return nil, nil, err
}
// Convert the markdown body of each control to HTML
for _, n := range modelData.Controls {
b := []byte(n.Body)
n.BodyHTML = template.HTML(blackfriday.Run(b))
}
addStats(modelData, renderData)
return modelData, renderData, nil
}
@@ -121,13 +122,14 @@ func loadWithStats() (*model.Data, *renderData, error) {
func addStats(modelData *model.Data, renderData *renderData) {
stats := &stats{}
satisfied := model.ControlsSatisfied(modelData)
satisfied := model.CriteriaSatisfied(modelData)
stats.ControlsTotal += len(renderData.Controls)
for _, std := range renderData.Standards {
stats.ControlsTotal += len(std.Controls)
for controlKey := range std.Controls {
if _, ok := satisfied[controlKey]; ok {
stats.ControlsSatisfied++
for _, std := range renderData.Frameworks {
stats.CriteriaTotal += len(std.Criteria)
for criteriaKey := range std.Criteria{
if _, ok := satisfied[criteriaKey]; ok {
stats.CriteriaSatisfied++
}
}
}

View File

@@ -114,7 +114,7 @@ func preprocessDoc(data *renderData, pol *model.Document, fullPath string) error
for standard, keys := range pol.Satisfies {
rows += fmt.Sprintf("| %s | %s |\n", standard, strings.Join(keys, ", "))
}
satisfiesTable = fmt.Sprintf("|Standard|Controls Satisfied|\n|-------+--------------------------------------------|\n%s\nTable: Control satisfaction\n", rows)
satisfiesTable = fmt.Sprintf("|Framework|Criteria Satisfied|\n|-------+--------------------------------------------|\n%s\nTable: Criterion satisfaction\n", rows)
}
if len(pol.Revisions) > 0 {

View File

@@ -16,6 +16,7 @@ func watch(errCh chan error) {
b.Add("./templates/")
b.Add("./narratives/")
b.Add("./policies/")
b.Add("./controls/")
b.Add("./procedures/")
b.Add("./.comply/")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
# Controls
Controls explicitly state a specific action that the organization will take to enforce a Policy goal.
## Format
```
name: Access Control Procedures
family: Access Control
identifier: AC-2
governingPolicy:
- policyName: Access Onboarding and Termination
policyID: SDM-AOTP
policyClause: 1.1
owner: Director, Security & Compliance
published: 2020-01-01
targets:
TSC 2017:
- CC6.1
- CC6.2
- CC6.3
NIST 800-53:
- AC-1
revisions:
- date: Sep 1 2020
comment: Initial documentation of control
---
1. Develop, document, and disseminate to all employees:
1. Organizational access control policy that:
1. Addresses purpose, scope, roles, responsibilities, management commitment, coordination among organizational entities, and compliance; and
```

View File

View File

@@ -66,7 +66,7 @@ html lang=en
a onclick="javascript:show('procedures')" Procedures
li.top-nav.standards
strong
a onclick="javascript:show('standards')" Standards
a onclick="javascript:show('frameworks')" Frameworks
/ li.top-nav.evidence
/ a onclick="javascript:show('evidence')" Evidence Vault
#overview.section.top-nav.container.content
@@ -76,17 +76,17 @@ html lang=en
.columns.is-vcentered
.column.is-one-third
div
p.subtitle.is-3.has-text-centered Control Tracking
p.subtitle.is-3.has-text-centered CriterionTracking
.column.has-text-centered
div
p.heading Satisfied Controls
p.heading Satisfied Criteria
p.title
{{.Stats.ControlsSatisfied}}
{{.Stats.CriteriaSatisfied}}
.column.has-text-centered
div
p.heading Total Controls
p.title
{{.Stats.ControlsTotal}}
{{.Stats.CriteriaTotal}}
.columns.is-vcentered
.column.is-one-third
div
@@ -187,23 +187,23 @@ html lang=en
td On demand
{{end}}
{{end}}
#standards.section.top-nav.container.content
#frameworks.section.top-nav.container.content
blockquote
h3
p
strong Standards
| specify the controls satisfied by the compliance program.
strong Framework Targets
| specify the Frameworks and Framework Criteria targeted by the compliance program.
table.table.is-size-4.is-fullwidth
thead
tr
th Control Key
th CriterionKey
th Name
th Satisfied?
th Satisfied By
tbody
{{range .Controls }}
{{range .Criteria}}
tr
td {{.ControlKey}}
td {{.criteriaKey}}
td
strong {{.Name}}
.subtitle {{.Description}}
@@ -234,4 +234,4 @@ html lang=en
show(destination)
}
}
}
}

View File

@@ -10,7 +10,7 @@ Compliance documents are organized as follows:
narratives/ Narratives provide an overview of the organization and the compliance environment.
policies/ Policies govern the behavior of employees and contractors.
procedures/ Procedures prescribe specific steps that are taken in response to key events.
standards/ Standards specify the controls satisfied by the compliance program.
frameworks/ Frameworks specify the control criteria targeted by the compliance program.
templates/ Templates control the output format of the HTML Dashboard and PDF assets.
```
@@ -57,4 +57,4 @@ comply build
# publish static site from output/ directory
upload.sh output/
```
```

View File

@@ -0,0 +1,33 @@
# Controls
Controls explicitly state a specific action that the organization will take to enforce a Policy goal.
## Format
```
name: Access Control Procedures
family: Access Control
identifier: AC-2
governingPolicy:
- policyName: Access Onboarding and Termination
policyID: SDM-AOTP
policyClause: 1.1
owner: Director, Security & Compliance
published: 2020-01-01
targets:
TSC 2017:
- CC6.1
- CC6.2
- CC6.3
NIST 800-53:
- AC-1
revisions:
- date: Sep 1 2020
comment: Initial documentation of control
---
1. Develop, document, and disseminate to all employees:
1. Organizational access control policy that:
1. Addresses purpose, scope, roles, responsibilities, management commitment, coordination among organizational entities, and compliance; and
```

View File

@@ -1,5 +1,5 @@
# Standards
# Frameworks
All `yaml` files in this directory are assumed to conform to https://github.com/opencontrol/schemas/tree/master/kwalify/standard
Adjust the target standard for this project by adding or removing line-items within each file, or adding/removing a standard file entirely.
Adjust the target standard for this project by adding or removing line-items within each file, or adding/removing a standard file entirely.

View File

@@ -1,4 +1,4 @@
name: Control Environment Narrative
name: CriterionEnvironment Narrative
acronym: CEN
satisfies:
TSC:
@@ -15,7 +15,7 @@ majorRevisions:
comment: Initial document
---
# Control Environment Narrative
# CriterionEnvironment Narrative
The following provides a description of the control structure of {{.Name}}.
@@ -34,7 +34,7 @@ The intent of this description is to enumerate the logical, policy, and procedur
{{.Name}} employs several policy controls to protect confidential data and ensure normal operation of its core product. These policies include, but are not limited to:
- Access Control Policy
- Access CriterionPolicy
- Encryption Policy
- Office Security Policy
- Password Policy

View File

View File

@@ -66,7 +66,7 @@ html lang=en
a onclick="javascript:show('procedures')" Procedures
li.top-nav.standards
strong
a onclick="javascript:show('standards')" Standards
a onclick="javascript:show('frameworks')" Frameworks
/ li.top-nav.evidence
/ a onclick="javascript:show('evidence')" Evidence Vault
#overview.section.top-nav.container.content
@@ -76,17 +76,17 @@ html lang=en
.columns.is-vcentered
.column.is-one-third
div
p.subtitle.is-3.has-text-centered Control Tracking
p.subtitle.is-3.has-text-centered CriterionTracking
.column.has-text-centered
div
p.heading Satisfied Controls
p.heading Satisfied Criteria
p.title
{{.Stats.ControlsSatisfied}}
{{.Stats.CriteriaSatisfied}}
.column.has-text-centered
div
p.heading Total Controls
p.title
{{.Stats.ControlsTotal}}
{{.Stats.CriteriaTotal}}
.columns.is-vcentered
.column.is-one-third
div
@@ -187,23 +187,23 @@ html lang=en
td On demand
{{end}}
{{end}}
#standards.section.top-nav.container.content
#frameworks.section.top-nav.container.content
blockquote
h3
p
strong Standards
| specify the controls satisfied by the compliance program.
strong Framework Targets
| specify the Frameworks and Framework Criteria targeted by the compliance program.
table.table.is-size-4.is-fullwidth
thead
tr
th Control Key
th CriterionKey
th Name
th Satisfied?
th Satisfied By
tbody
{{range .Controls }}
{{range .Criteria}}
tr
td {{.ControlKey}}
td {{.criteriaKey}}
td
strong {{.Name}}
.subtitle {{.Description}}
@@ -234,4 +234,4 @@ html lang=en
show(destination)
}
}
}
}