Skip to content

Commit

Permalink
Adding tests docs, fixing makefile tasks (Checkmarx#2907)
Browse files Browse the repository at this point in the history
Signed-off-by: Rogério Peixoto <[email protected]>
  • Loading branch information
rogeriopeixotocx authored Apr 20, 2021
1 parent 19d9872 commit 2d46173
Show file tree
Hide file tree
Showing 10 changed files with 301 additions and 34 deletions.
130 changes: 130 additions & 0 deletions .github/scripts/get-coverage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#!/usr/bin/env python3
import itertools
import os
import re
import sys
import typing
from argparse import ArgumentParser


class Arguments:
coverage_file: str


class BaseItem:
def __init__(self, **kw):
"""Inits the BaseItem object."""
self.__dict__.update(kw)

def __repr__(self):
"""Converts the BaseItem object to string."""
return f"{self.__class__.__name__}(**{self.__dict__!r})"


class LineStats(BaseItem):
filepath: str
line_from: int
line_to: int
count_covered: int
covered: bool


class FileStats(BaseItem):
filepath: str
total_lines: int
covered_lines: int
uncovered_lines: int

@property
def coverage(self) -> float:
try:
return 100*self.covered_lines/self.total_lines
except ZeroDivisionError:
return 100.


def parse_args() -> Arguments:
args = ArgumentParser()
args.add_argument("coverage_file")
return typing.cast(Arguments, args.parse_args(sys.argv[1:]))


def load_coverage(args: Arguments) -> typing.List[LineStats]:
pat = re.compile(
r"([^:]*):"
r"(\d+)\.\d*,(\d+)\.\d* (\d+) (\d+)"
)

out = []

with open(args.coverage_file, "r") as fd:
next(fd)

for line in fd:
attrs = dict(zip(
["filename", "line_from", "line_to", "count_covered", "covered"],
pat.findall(line)[0]
))

for col in ["line_from", "line_to", "count_covered"]:
attrs[col] = int(attrs[col])

attrs["covered"] = bool(int(attrs["covered"]))

el = LineStats(**attrs)
out.append(el)

return out


def calc_file_stats(lines: typing.List[LineStats]) -> typing.List[FileStats]:
lines = lines[:]
def key(i): return i.filename
lines.sort(key=key)

out = []

for filename, group in itertools.groupby(lines, key=key):
group = list(group)
group.sort(key=lambda i: i.line_to)

f = FileStats(filepath=filename)
f.total_lines = sum([
i.count_covered
for i in group
])
f.covered_lines = sum([
i.count_covered
for i in group
if i.covered
])
f.uncovered_lines = sum([
i.count_covered
for i in group
if not i.covered
])
out.append(f)
return out


def total_cov(data: typing.List[FileStats]) -> float:
covered = 0
total = 0
for item in data:
covered += item.covered_lines
total += item.total_lines
return 100*covered/total


def main():
args = parse_args()
lines = load_coverage(args)
stats = calc_file_stats(lines)
total = total_cov(stats)
if os.environ.get('GITHUB_RUN_ID'):
print(f"::set-output name=coverage::{total}")
print(f"Total coverage: {total}")


if __name__ == '__main__':
main()
12 changes: 12 additions & 0 deletions .github/workflows/go-ci-metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,15 @@ jobs:
run: sudo apt install zsh
- name: Run test metrics script
run: zsh .github/scripts/get-test-metrics.sh
coverage:
name: test-coverage
runs-on: ubuntu-latest
steps:
- name: Checkout Source
uses: actions/checkout@v2
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: 1.16.x
- name: Run test metrics script
run: make test-coverage-report
16 changes: 3 additions & 13 deletions .github/workflows/go-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,24 +70,14 @@ jobs:
${{ runner.OS }}-build-${{ env.cache-name }}
${{ runner.OS }}-build-
${{ runner.OS }}-
- name: Get go-junit-report module
env:
GO111MODULE: off
run: |
go get -u github.com/jstemmer/go-junit-report
- name: Get Modules
run: |
go mod vendor
- name: Test and Generate Report
run: |
set +o pipefail
go test -mod=vendor -v $(go list ./... | grep -v e2e) -count=1 -coverprofile cover.out 2>&1 | go-junit-report -set-exit-code -go-version ${{ matrix.go-version }} -package-name "github.com/Checkmarx/kics/test" > test-report-${{ matrix.os }}.xml
- name: Archive unit tests report
uses: actions/upload-artifact@v2
with:
name: unit-tests-report-${{ matrix.os }}-${{ github.event.pull_request.head.sha }}
path: |
test-report*.xml
echo "::group::Unit Tests"
go test -mod=vendor -v $(go list ./... | grep -v e2e) -count=1 -coverprofile=cover.out
echo "::endgroup::"
security-scan:
name: security-scan
runs-on: ubuntu-latest
Expand Down
23 changes: 20 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,29 @@ test-short: generate
@go test -short ./...

.PHONY: test
test: ## Run tests with race detector and code covarage
test: generate
test: ## Run all tests
test: test-race-cover test-e2e
$(call print-target)
@go test -race -covermode=atomic -coverprofile=coverage.out ./...

.PHONY: test-race-cover
test-race-cover: ## Run tests with race detector and code coverage
test-race-cover: generate
$(call print-target)
@go test -race -covermode=atomic -coverprofile=coverage.out $(shell go list ./... | grep -v e2e)

.PHONY: test-coverage-report
test-coverage-report: ## Run unit tests and generate test coverage report
test-coverage-report: test-race-cover
@python3 .github/scripts/get-coverage.py coverage.out
@echo "Generating coverage.html"
@go tool cover -html=coverage.out -o coverage.html

.PHONY: test-e2e
test-e2e: ## Run E2E tests
test-e2e: build
$(call print-target)
E2E_KICS_BINARY=$(PWD)/bin/kics go test "github.com/Checkmarx/kics/e2e" -v

.PHONY: cover
cover: ## generate coverage report
cover: test
Expand Down
34 changes: 17 additions & 17 deletions docs/creating-queries.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Create a new Query
## Create a new Query

The queries are written in Rego and our internal parser transforms every IaC file that supports into a universal JSON format. This way anyone can start working on a query by picking up a small sample of the vulnerability that the query should target, and convert this sample, that can be a .tf or .yaml file, to our JSON structure JSON. To convert the sample you can run the following command:

Expand All @@ -16,12 +16,12 @@ To test and debug there are two ways:
- [Using Rego playground](https://play.openpolicyagent.org/)
- [Install Open Policy Agent extension](https://marketplace.visualstudio.com/items?itemName=tsandall.opa) in VS Code and create a simple folder with a .rego file for the query and a input.json for the sample to test against

#### Testing
#### Testing

For a query to be considered complete, it must be compliant with at least one positive and one negative test case. To run the unit tests you can run this command:

```bash
go test -mod=vendor -v -cover ./... -count=1
make test
```
Check if the new test was added correctly and if all tests are passing locally. If succeeds, a Pull Request can now be created.

Expand All @@ -36,19 +36,19 @@ go run ./cmd/console/main.go generate-id
- `queryName` describes the name of the vulnerability
- `severity` can be filled with `HIGH`, `MEDIUM`, `LOW` or `INFO`
- `category` pick one of the following:
- Access Control
- Availability
- Backup
- Best Practices
- Build Process
- Encryption
- Insecure Configurations
- Insecure Defaults
- Networking and Firewall
- Observability
- Resource Management
- Secret Management
- Supply-Chain
- Access Control
- Availability
- Backup
- Best Practices
- Build Process
- Encryption
- Insecure Configurations
- Insecure Defaults
- Networking and Firewall
- Observability
- Resource Management
- Secret Management
- Supply-Chain
- `descriptionText` should explain with detail the vulnerability and if possible provide a way to remediate
- `descriptionUrl` points to the official documentation about the resource being targeted
- `platform` query target platform (e.g. Terraform, Kubernetes, etc.)
- `platform` query target platform (e.g. Terraform, Kubernetes, etc.)
4 changes: 4 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ docker pull checkmarx/kics:latest
docker run -v {​​​​path_to_host_folder_to_scan}​​​​:/path checkmarx/kics:latest scan -p "/path" -o "/path/results.json"
```

We also provide alpine based docker image with `latest-alpine`.

You can see the list of available tags in [dockerhub](https://hub.docker.com/r/checkmarx/kics/tags?page=1&ordering=-name)

You can provide your own path to the queries directory with `-q` CLI option (see CLI Options section below), otherwise the default directory will be used The default *./assets/queries* is built-in in the image.

#### One-liner Install Script
Expand Down
2 changes: 1 addition & 1 deletion docs/queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ CxPolicy [ result ] {
resource := input.document[i].resource.aws_s3_bucket[name]
role = "public-read"
resource.acl == role
result := {
"documentId": input.document[i].id,
"searchKey": sprintf("aws_s3_bucket[%s].acl", [name]),
Expand Down
84 changes: 84 additions & 0 deletions e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# E2E tests

The purpose of this docs is to describe KICS' E2E test suite

## Getting Started

There are several ways to execute the E2E tests.

### TLDR

This steps will build the kics and then run the E2E using the built binary (placed by default under `${PWD}/bin/kics`)

```bash
make test-e2e
```

### Step by Step

These steps will build the kics and then run the E2E using the built binary.

```bash
go build -o ./bin/kics cmd/console/main.go
```

If you want to provide a version:
```bash
go build -o ./bin/kics -ldflags "-X github.com/Checkmarx/kics/internal/constants.Version=$(git rev-parse --short HEAD) cmd/console/main.go
```
and then:
```bash
E2E_KICS_BINARY=./bin/kics go test "github.com/Checkmarx/kics/e2e" -v
```
## Test Scenarios
Test scenarios are defined as follows:
```go
var tests = []struct {
name string
args args
wantStatus int
removePayload []string
}{
// E2E-CLI-005 - KICS scan with -- payload-path flag should create a file with the
// passed name containing the payload of the files scanned
{
name: "E2E-CLI-005",
args: args{
// These are CLI arguments passed down to the KICS binary
args: []cmdArgs{
[]string{"scan", "--silent", "-q", "../assets/queries", "-p", "fixtures/samples/terraform.tf",
"--payload-path", "fixtures/payload.json", "-q", "../assets/queries"},
},
// this is a reference to a fixture placed under e2e/fixtures that contains the expected stdout output
expectedOut: []string{
"E2E_CLI_005",
},
// this is a reference to a fixture placed under e2e/fixtures that contains the expected KICS' payload
expectedPayload: []string{
"E2E_CLI_005_PAYLOAD",
},
},
wantStatus: 0,
// we should cleanup the payload after running this scenario
removePayload: []string{"payload.json"},
},
}
```
E2E tests are skiped in short mode:
```go
func Test_E2E_CLI(t *testing.T) {
kicsPath := getKICSBinaryPath("")
if testing.Short() {
t.Skip("skipping E2E tests in short mode.")
}
//...
}
```
4 changes: 4 additions & 0 deletions e2e/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ var tests = []struct {
func Test_E2E_CLI(t *testing.T) {
kicsPath := getKICSBinaryPath("")

if testing.Short() {
t.Skip("skipping E2E tests in short mode.")
}

for _, tt := range tests {
for arg := range tt.args.args {
t.Run(fmt.Sprintf("%s_%d", tt.name, arg), func(t *testing.T) {
Expand Down
Loading

0 comments on commit 2d46173

Please sign in to comment.