mirror of
https://github.com/strongdm/comply
synced 2024-11-25 09:04:54 +00:00
Add tests (#48)
This commit is contained in:
parent
53a65f6c00
commit
1c80e1ce66
94
fixtures/narratives/invalid-control.md
Normal file
94
fixtures/narratives/invalid-control.md
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
name: Control Environment Narrative
|
||||||
|
acronym: CEN
|
||||||
|
satisfies:
|
||||||
|
TSC:
|
||||||
|
- CC2.1
|
||||||
|
- CC2.2
|
||||||
|
- CC2.3
|
||||||
|
- CC4.1
|
||||||
|
- CC4.2
|
||||||
|
- CC5.1
|
||||||
|
- CC5.2
|
||||||
|
- CC5.3
|
||||||
|
majorRevisions:
|
||||||
|
- date: Jun 1 2018
|
||||||
|
comment: Initial document
|
||||||
|
---
|
||||||
|
|
||||||
|
# Control Environment Narrative
|
||||||
|
|
||||||
|
The following provides a description of the control structure of {{.Name}}.
|
||||||
|
|
||||||
|
The intent of this description is to enumerate the logical, policy, and procedural controls that serve to monitor {{.Name}}'s application and data security. Changes uncovered by these procedures in the logical, policy, procedural, or customer environment are addressed by remediations specific to the noted change.
|
||||||
|
|
||||||
|
# Logical Controls
|
||||||
|
|
||||||
|
{{.Name}} employs several logical controls to protect confidential data and ensure normal operation of its core product.
|
||||||
|
|
||||||
|
- Mandatory data encryption at rest and in motion
|
||||||
|
- Multi-factor authentication for access to cloud infrastructure
|
||||||
|
- Activity and anomaly monitoring on production systems
|
||||||
|
- Vulnerability management program
|
||||||
|
|
||||||
|
# Policy Controls
|
||||||
|
|
||||||
|
{{.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
|
||||||
|
- Encryption Policy
|
||||||
|
- Office Security Policy
|
||||||
|
- Password Policy
|
||||||
|
- Policy Training Policy
|
||||||
|
- Vendor Policy
|
||||||
|
- Workstation Policy
|
||||||
|
|
||||||
|
# Procedural Controls
|
||||||
|
|
||||||
|
{{.Name}} has numerous scheduled procedures to monitor and tune the effectiveness of ongoing security controls, and a series of event-driven procedures to respond to security-related events.
|
||||||
|
|
||||||
|
TODO: Finalize these lists
|
||||||
|
|
||||||
|
## Scheduled Security and Audit Procedures
|
||||||
|
|
||||||
|
- Review Access [quarterly]
|
||||||
|
- Review Security Logs [weekly]
|
||||||
|
- Review Cyber Risk Assessment (enumerate possible compromise scenarios) [quarterly]
|
||||||
|
- Review Data Classification [quarterly]
|
||||||
|
- Backup Testing [quarterly]
|
||||||
|
- Disaster Recovery Testing [semi-annual]
|
||||||
|
- Review Devices & Workstations [quarterly]
|
||||||
|
- Review & Clear Low-Priority Alerts [weekly]
|
||||||
|
- Apply OS Patches [monthly]
|
||||||
|
- Verify Data Disposal per Retention Policy [quarterly]
|
||||||
|
- Conduct Security Training [annual]
|
||||||
|
- Review Security Monitoring and Alerting Configuration [quarterly]
|
||||||
|
- Penetration Test [annual]
|
||||||
|
- Whitebox Security Review [annual]
|
||||||
|
- SOC2 Audit [annual]
|
||||||
|
|
||||||
|
## Event-Driven Security and Audit Procedures
|
||||||
|
|
||||||
|
- Onboard Employee
|
||||||
|
- Offboard Employee
|
||||||
|
- Investigate Security Alert
|
||||||
|
- Investigate Security Incident
|
||||||
|
|
||||||
|
# Remediations
|
||||||
|
|
||||||
|
{{.Name}} uses the outcomes of the aforementioned controls and procedures to identify shortcomings in the existing control environment. Once identified, these shortcomes are remediated by improving existing controls and procedures, and creating new controls and procedures as needed.
|
||||||
|
|
||||||
|
# Communications
|
||||||
|
|
||||||
|
{{.Name}} communicates relevant information regarding the functioning of the above controls with internal and external parties on an as-needed basis and according to statutory requirements.
|
||||||
|
|
||||||
|
## Internal
|
||||||
|
|
||||||
|
{{.Name}} communicates control outcomes, anomalies, and remediations internally using the following channels:
|
||||||
|
|
||||||
|
- Slack
|
||||||
|
- Email
|
||||||
|
- Github ticketing
|
||||||
|
|
||||||
|
## External
|
||||||
|
|
||||||
|
{{.Name}} communicates relevant control-related information to external parties including shareholders, customers, contractors, regulators, and government entities as needed according to contractual and regulatory/statutory obligation.
|
@ -82,7 +82,7 @@ func Exists() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Config is the parsed contents of ProjectRoot()/config.yml.
|
// Config is the parsed contents of ProjectRoot()/config.yml.
|
||||||
func Config() *Project {
|
var Config = func() *Project {
|
||||||
p := Project{}
|
p := Project{}
|
||||||
cfgBytes, err := ioutil.ReadFile(filepath.Join(ProjectRoot(), "comply.yml"))
|
cfgBytes, err := ioutil.ReadFile(filepath.Join(ProjectRoot(), "comply.yml"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
78
internal/model/fs_test.go
Normal file
78
internal/model/fs_test.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/strongdm/comply/internal/config"
|
||||||
|
"github.com/strongdm/comply/internal/path"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestReadNarratives calls model.ReadNarratives checking for a valid return value.
|
||||||
|
func TestReadNarratives(t *testing.T) {
|
||||||
|
mockConfig()
|
||||||
|
path.Narratives = func() ([]path.File, error) {
|
||||||
|
filePath := fmt.Sprintf("%s/narratives/control.md", getRootPath())
|
||||||
|
fileInfo, _ := os.Lstat(filePath)
|
||||||
|
return []path.File{
|
||||||
|
{FullPath: filePath, Info: fileInfo},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := ReadNarratives()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`ReadNarratives() returned an error %v`, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestReadNarratives calls model.ReadNarratives checking for a valid return when
|
||||||
|
// there are no narratives to process
|
||||||
|
func TestReadNarrativesWhenThereAreNoNarratives(t *testing.T) {
|
||||||
|
mockConfig()
|
||||||
|
path.Narratives = func() ([]path.File, error) {
|
||||||
|
return []path.File{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := ReadNarratives()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(`ReadNarratives() returned an error %v`, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestReadNarratives calls model.ReadNarratives checking for an error return when
|
||||||
|
// there is an invalid narrative
|
||||||
|
func TestReadNarrativesFailsWhenInvalidNarrative(t *testing.T) {
|
||||||
|
mockConfig()
|
||||||
|
path.Narratives = func() ([]path.File, error) {
|
||||||
|
filePath := fmt.Sprintf("%s/../fixtures/narratives/invalid-control.md", getRootPath())
|
||||||
|
fileInfo, _ := os.Lstat(filePath)
|
||||||
|
return []path.File{
|
||||||
|
{FullPath: filePath, Info: fileInfo},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := ReadNarratives()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(`ReadNarratives() was expected to fail`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockConfig() {
|
||||||
|
config.Config = func() *config.Project {
|
||||||
|
p := config.Project{}
|
||||||
|
cfgBytes, _ := ioutil.ReadFile(filepath.Join(getRootPath(), "comply.yml.example"))
|
||||||
|
yaml.Unmarshal(cfgBytes, &p)
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRootPath() string {
|
||||||
|
_, fileName, _, _ := runtime.Caller(0)
|
||||||
|
fileDir := filepath.Dir(fileName)
|
||||||
|
return fmt.Sprintf("%s/../../example", fileDir)
|
||||||
|
}
|
@ -9,22 +9,22 @@ import (
|
|||||||
func TestMarshal(t *testing.T) {
|
func TestMarshal(t *testing.T) {
|
||||||
d := Data{
|
d := Data{
|
||||||
Tickets: []*Ticket{
|
Tickets: []*Ticket{
|
||||||
&Ticket{
|
{
|
||||||
ID: "t1",
|
ID: "t1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Audits: []*Audit{
|
Audits: []*Audit{
|
||||||
&Audit{
|
{
|
||||||
ID: "a1",
|
ID: "a1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Procedures: []*Procedure{
|
Procedures: []*Procedure{
|
||||||
&Procedure{
|
{
|
||||||
Code: "pro1",
|
ID: "pro1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Policies: []*Policy{
|
Policies: []*Document{
|
||||||
&Policy{
|
{
|
||||||
Name: "pol1",
|
Name: "pol1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -40,5 +40,4 @@ func TestMarshal(t *testing.T) {
|
|||||||
!strings.Contains(encoded, "pol1") {
|
!strings.Contains(encoded, "pol1") {
|
||||||
t.Error("identifier not found in marshalled string")
|
t.Error("identifier not found in marshalled string")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,22 +16,22 @@ type File struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Standards lists all standard files.
|
// Standards lists all standard files.
|
||||||
func Standards() ([]File, error) {
|
var Standards = func() ([]File, error) {
|
||||||
return filesFor("standards", "yml")
|
return filesFor("standards", "yml")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Narratives lists all narrative files.
|
// Narratives lists all narrative files.
|
||||||
func Narratives() ([]File, error) {
|
var Narratives = func() ([]File, error) {
|
||||||
return filesFor("narratives", "md")
|
return filesFor("narratives", "md")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Policies lists all policy files.
|
// Policies lists all policy files.
|
||||||
func Policies() ([]File, error) {
|
var Policies = func() ([]File, error) {
|
||||||
return filesFor("policies", "md")
|
return filesFor("policies", "md")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Procedures lists all procedure files.
|
// Procedures lists all procedure files.
|
||||||
func Procedures() ([]File, error) {
|
var Procedures = func() ([]File, error) {
|
||||||
return filesFor("procedures", "md")
|
return filesFor("procedures", "md")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user