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

1
go.mod
View File

@@ -33,6 +33,7 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/robfig/cron v1.2.0 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/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect
github.com/trivago/tgo v1.0.7 // 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/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/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-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/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/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/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 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 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.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/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/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/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= 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-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.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 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/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/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= 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.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 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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-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 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 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/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.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+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/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/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/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-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 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= 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/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 h1:tlXG832s5pa9x9Gs3Rp2rTvEqjiDEuETUOSfBEiTcns=
github.com/russross/blackfriday v1.5.3-0.20200218234912-41c5fccfd6f6/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 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/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/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/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/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/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/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/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.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=

View File

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

View File

@@ -1,18 +1,23 @@
package model package model
type Control struct { type Criterion struct {
Family string `yaml:"family"` Family string `yaml:"family"`
Name string `yaml:"name"` Name string `yaml:"name"`
Description string `yaml:"description"` Description string `yaml:"description"`
Satisfied bool
SatisfiedBy []string
} }
type Standard struct { type Framework struct {
Name string `yaml:"name"` 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 type Target map[string][]string
func ControlsSatisfied(data *Data) 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) satisfied := make(map[string][]string)
appendSatisfaction := func(in map[string][]string, k string, v 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 _, n := range data.Narratives {
for _, controlKeys := range n.Satisfies { for _, criteriaKeys := range n.Satisfies {
for _, key := range controlKeys { for _, key := range criteriaKeys {
satisfied[key] = appendSatisfaction(satisfied, key, n.OutputFilename) satisfied[key] = appendSatisfaction(satisfied, key, n.OutputFilename)
} }
} }
} }
for _, n := range data.Policies { for _, n := range data.Policies {
for _, controlKeys := range n.Satisfies { for _, criteriaKeys := range n.Satisfies {
for _, key := range controlKeys { 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) satisfied[key] = appendSatisfaction(satisfied, key, n.OutputFilename)
} }
} }
} }
for _, n := range data.Procedures { for _, n := range data.Procedures {
for _, controlKeys := range n.Satisfies { for _, criteriaKeys := range n.Satisfies {
for _, key := range controlKeys { for _, key := range criteriaKeys {
satisfied[key] = appendSatisfaction(satisfied, key, n.OutputFilename) satisfied[key] = appendSatisfaction(satisfied, key, n.OutputFilename)
} }
} }

View File

@@ -26,11 +26,15 @@ func ReadData() (*Data, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
controls, err := ReadControls()
if err != nil {
return nil, err
}
procedures, err := ReadProcedures() procedures, err := ReadProcedures()
if err != nil { if err != nil {
return nil, err return nil, err
} }
standards, err := ReadStandards() frameworks, err := ReadFrameworks()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -39,8 +43,9 @@ func ReadData() (*Data, error) {
Tickets: tickets, Tickets: tickets,
Narratives: narratives, Narratives: narratives,
Policies: policies, Policies: policies,
Controls: controls,
Procedures: procedures, Procedures: procedures,
Standards: standards, Frameworks: frameworks,
}, nil }, nil
} }
@@ -67,27 +72,27 @@ func tickets(rawTickets []string) ([]*Ticket, error) {
return tickets, nil return tickets, nil
} }
// ReadStandards loads standard definitions from the filesystem. // ReadFrameworks loads standard definitions from the filesystem.
func ReadStandards() ([]*Standard, error) { func ReadFrameworks() ([]*Framework, error) {
var standards []*Standard var frameworks []*Framework
files, err := path.Standards() files, err := path.Frameworks()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "unable to enumerate paths") return nil, errors.Wrap(err, "unable to enumerate paths")
} }
for _, f := range files { for _, f := range files {
s := &Standard{} s := &Framework{}
sBytes, err := ioutil.ReadFile(f.FullPath) sBytes, err := ioutil.ReadFile(f.FullPath)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "unable to read "+f.FullPath) return nil, errors.Wrap(err, "unable to read "+f.FullPath)
} }
yaml.Unmarshal(sBytes, &s) 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. // ReadNarratives loads narrative descriptions from the filesystem.
@@ -166,6 +171,31 @@ func ReadPolicies() ([]*Document, error) {
return policies, nil 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 { type metadataMarkdown struct {
yaml string yaml string
body string body string

View File

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

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"` Revisions []Revision `yaml:"majorRevisions"`
Satisfies Satisfaction `yaml:"satisfies"` Satisfies Satisfaction `yaml:"satisfies"`
Targets Target `yaml:"targets"`
FullPath string FullPath string
OutputFilename string OutputFilename string
ModifiedAt time.Time ModifiedAt time.Time

View File

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

View File

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

View File

@@ -114,7 +114,7 @@ func preprocessDoc(data *renderData, pol *model.Document, fullPath string) error
for standard, keys := range pol.Satisfies { for standard, keys := range pol.Satisfies {
rows += fmt.Sprintf("| %s | %s |\n", standard, strings.Join(keys, ", ")) 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 { if len(pol.Revisions) > 0 {

View File

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

View File

@@ -10,7 +10,7 @@ Compliance documents are organized as follows:
narratives/ Narratives provide an overview of the organization and the compliance environment. narratives/ Narratives provide an overview of the organization and the compliance environment.
policies/ Policies govern the behavior of employees and contractors. policies/ Policies govern the behavior of employees and contractors.
procedures/ Procedures prescribe specific steps that are taken in response to key events. 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. templates/ Templates control the output format of the HTML Dashboard and PDF assets.
``` ```

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,4 +1,4 @@
# Standards # Frameworks
All `yaml` files in this directory are assumed to conform to https://github.com/opencontrol/schemas/tree/master/kwalify/standard All `yaml` files in this directory are assumed to conform to https://github.com/opencontrol/schemas/tree/master/kwalify/standard

View File

@@ -1,4 +1,4 @@
name: Control Environment Narrative name: CriterionEnvironment Narrative
acronym: CEN acronym: CEN
satisfies: satisfies:
TSC: TSC:
@@ -15,7 +15,7 @@ majorRevisions:
comment: Initial document comment: Initial document
--- ---
# Control Environment Narrative # CriterionEnvironment Narrative
The following provides a description of the control structure of {{.Name}}. 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: {{.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 - Encryption Policy
- Office Security Policy - Office Security Policy
- Password Policy - Password Policy

View File

View File

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