Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add adoc report generator #87

Merged
merged 16 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ risks.json
technical-assets.json
stats.json
.vscode
.task

# Binaries for programs and plugins
*.exe
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile.local
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ FROM alpine:latest as finalize
LABEL type="threagile"

# update vulnerable packages
RUN apk add libcrypto3=3.3.1-r0
RUN apk add libssl3=3.3.1-r0
RUN apk add libcrypto3=3.3.1-r3
RUN apk add libssl3=3.3.1-r3

# add certificates, graphviz, fonts
RUN apk add --update --no-cache ca-certificates
Expand Down
79 changes: 79 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
version: '3'

vars:
AD_GLOBAL_REQUIRES: --require asciidoctor-diagram --verbose --require asciidoctor-pdf --backend pdf --attribute allow-uri-read
CONTAINER: asciidoctor/docker-asciidoctor
# CONTAINER: docker-asciidoctor:local
DOCKER_ASCIIDOCTOR: docker run -it -u $(id -u):$(id -g) -v $(pwd):/documents/ {{.CONTAINER}} asciidoctor {{.AD_GLOBAL_REQUIRES}} --require asciidoctor-kroki
GIT_SHORT_SHA:
sh: git log -n 1 --format=%h

env:
GOOS: linux
GO111MODULE: on

tasks:
default:
deps: [convert-example-project-adoc-to-pdf]

build-and-test:
desc: build threagile and run tests
deps: [build-threagile, run-tests]

build-threagile:
desc: build threagile
sources:
- "**/*.go"
- exclude: "**/*_test.go"
generates:
- bin/threagile
vars:
GOFLAGS: -a -ldflags="-s -w -X main.buildTimestamp=$(shell date '+%Y%m%d%H%M%S')"
cmds:
- go mod download
- go build -o bin/threagile cmd/threagile/main.go

run-tests:
desc: run threagile tests
deps: [build-threagile]
cmds:
- go test ./...

convert-example-project-adoc-to-pdf:
desc: create example project and create a pdf from the asciidoctor output
deps: [create-example-project]
dir: /tmp/threagile-test/adocReport
cmds:
- "{{.DOCKER_ASCIIDOCTOR}} --attribute DOC_VERSION={{.GIT_SHORT_SHA}} --attribute pdf-themesdir=/documents/theme --attribute pdf-theme=pdf /documents/00_main.adoc"
- cp /tmp/threagile-test/adocReport/00_main.pdf /tmp/report.pdf
- echo "Open report with \"xdg-open /tmp/report.pdf\""

create-example-project:
desc: create the example project
deps: [build-threagile]
env:
YAML: ./demo/example/threagile.yaml
cmds:
- mkdir -p /tmp/threagile-test
- ./bin/threagile analyze-model
--model ${YAML}
--output /tmp/threagile-test
--ignore-orphaned-risk-tracking
--app-dir .
--generate-report-adoc
--generate-report-pdf=0
# --background ./report/template/background.pdf

golangci-lint:
desc: run golangci-lint on current code
cmds:
- docker run --rm -it -v $(pwd):/app -w /app golangci/golangci-lint golangci-lint run -v

gosec:
desc: run securego/gosec
cmds:
- docker run --rm -it -v $(pwd):/app -w /app securego/gosec /app/...

linting:
desc: all linting jobs
deps: [golangci-lint, gosec]
83 changes: 83 additions & 0 deletions doc/asciidoctor-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Asciidoctor Report Generator
The Asciidoctor Report Generator generates the report into asciidoctor files together with a default theme. This enables us to have a full flexibility of the generated output. One has the ability to create its own mainfile and include only the chapters that are needed for a special report. Also the themability of the Report is given. by simply using a different theme than the standard, but keep in mind that the roles from the default theme have to exist otherwise coloring of some texts might not match.

## Report Generation

To additionaly generate the adoc report you have to run threagile with the option `--generate-report-adoc` instead of `--generate-report-pdf` then a folder adocReport will be generated inside the output folder. After that you can run your favorite asciidoctor command to build a pdf or html page or whatever you want from it. The below example uses the asciidoctor docker container to create a pdf.

1. Step, create adoc report
```
$ mkdir -p /tmp/threagile-test
$ ./bin/threagile analyze-model \
--model ./demo/example/threagile.yaml \
--output /tmp/threagile-test \
--ignore-orphaned-risk-tracking \
--app-dir . \
--generate-report-pdf=0 \
--generate-report-adoc
```
2. Step, create pdf from adoc
```
$ docker run -it -u $(id -u):$(id -g) -v /tmp/threagile-test/adocReport:/documents/ asciidoctor/docker-asciidoctor \
asciidoctor --verbose --require asciidoctor-pdf --backend pdf \
--attribute allow-uri-read --require asciidoctor-kroki \
--attribute DOC_VERSION=V1.0 \
--attribute pdf-themesdir=/documents/theme --attribute pdf-theme=pdf \
/documents/00_main.adoc
```

The generated report can then be found at `/tmp/threagile-test/adocReport/00_main.pdf`

## Report Generation with custom main

1. Generate the adoc report
2. Create a custom adoc file and use the only the generated parts (the example uses echo, bug copying from somewhere else might be better)
3. create pdf from it

For example:
```
$ mkdir -p /tmp/threagile-test
$ ./bin/threagile analyze-model \
--model ./demo/example/threagile.yaml \
--output /tmp/threagile-test \
--ignore-orphaned-risk-tracking \
--app-dir . \
--generate-report-pdf=0 \
--generate-report-adoc
$ echo "= Custom short threat model\n:title-page:\n:toc:\ninclude::03_RiskMitigationStatus.adoc[leveloffset=+1]\n<<<\ninclude::04_ImpactRemainingRisks.adoc[leveloffset=+1]" > /tmp/threagile-test/adocReport/my-main.adoc
$ docker run -it -u $(id -u):$(id -g) -v /tmp/threagile-test/adocReport:/documents/ asciidoctor/docker-asciidoctor \
asciidoctor --verbose --require asciidoctor-pdf --backend pdf \
--attribute allow-uri-read --require asciidoctor-kroki \
--attribute DOC_VERSION=V1.0 \
--attribute pdf-themesdir=/documents/theme --attribute pdf-theme=pdf \
/documents/my-main.adoc
```

The generated report can then be found at `/tmp/threagile-test/adocReport/my-main.pdf`

## Report Generation with custom theme

1. Generate the adoc report
2. Create a custom theme, and place it there. For simplicity a very simple one could already be found next to this documetnation (`custom-theme.yml`), keep in mind that the role's that are in it are essential. To take a good base you should take the created one found in `<outputFolder>/adocReport/theme/pdf.yml` and adjust it to your needs.
3. Create pdf from it

For example:
```
$ mkdir -p /tmp/threagile-test
$ ./bin/threagile analyze-model \
--model ./demo/example/threagile.yaml \
--output /tmp/threagile-test \
--ignore-orphaned-risk-tracking \
--app-dir . \
--generate-report-pdf=0 \
--generate-report-adoc
$ cp doc/custom-theme.yml /tmp/threagile-test/adocReport/theme/my-pdf-theme.yml
$ docker run -it -u $(id -u):$(id -g) -v /tmp/threagile-test/adocReport:/documents/ asciidoctor/docker-asciidoctor \
asciidoctor --verbose --require asciidoctor-pdf --backend pdf \
--attribute allow-uri-read --require asciidoctor-kroki \
--attribute DOC_VERSION=V1.0 \
--attribute pdf-themesdir=/documents/theme --attribute pdf-theme=my-pdf \
/documents/00_main.adoc
```

The generated report can then be found at `/tmp/threagile-test/adocReport/00_main.pdf`
51 changes: 51 additions & 0 deletions doc/custom-theme.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
extends: default
footer:
height: 2cm
line-height: 1.2
recto:
center:
content: "My Custom Theme"
right:
content: "Page {page-number} / {page-count}"
verso:
center:
content: "{document-title}"
right:
content: |
Page {page-number} / {page-count}
role:
LowRisk:
font-color: #23465F
MediumRisk:
font-color: #C87832
ElevatedRisk:
font-color: #FF8E00
HighRisk:
font-color: #A0281E
CriticalRisk:
font-color: #FF2600
OutOfScope:
font-color: #7f7f7f
GreyText:
font-color: #505050
LightGreyText:
font-color: #646464
ModelFailure:
font-color: #945200
RiskStatusFalsePositive:
font-color: #666666
RiskStatusMitigated:
font-color: #008F00
RiskStatusInProgress:
font-color: #0000FF
RiskStatusAccepted:
font-color: #FF40FF
RiskStatusInDiscussion:
font-color: #FF9300
RiskStatusUnchecked:
font-color: #FF0000
Twilight:
font-color: #3A52C8
SmallGrey:
font-size: 0.5em
font-color: #505050
9 changes: 9 additions & 0 deletions internal/threagile/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type Config struct {
JsonTechnicalAssetsFilename string
JsonStatsFilename string
TemplateFilename string
ReportLogoImagePath string
TechnologyFilename string

RiskRulesPlugins []string
Expand Down Expand Up @@ -88,6 +89,7 @@ func (c *Config) Defaults(buildTimestamp string) *Config {
JsonTechnicalAssetsFilename: JsonTechnicalAssetsFilename,
JsonStatsFilename: JsonStatsFilename,
TemplateFilename: TemplateFilename,
ReportLogoImagePath: ReportLogoImagePath,
TechnologyFilename: "",

RiskRulesPlugins: make([]string, 0),
Expand Down Expand Up @@ -274,6 +276,9 @@ func (c *Config) Merge(config Config, values map[string]any) {
case strings.ToLower("TemplateFilename"):
c.TemplateFilename = config.TemplateFilename

case strings.ToLower("ReportLogoImagePath"):
c.TemplateFilename = config.ReportLogoImagePath

case strings.ToLower("TechnologyFilename"):
c.TechnologyFilename = config.TechnologyFilename

Expand Down Expand Up @@ -469,6 +474,10 @@ func (c *Config) GetTemplateFilename() string {
return c.TemplateFilename
}

func (c *Config) GetReportLogoImagePath() string {
return c.ReportLogoImagePath
}

func (c *Config) GetRiskRulesPlugins() []string {
return c.RiskRulesPlugins
}
Expand Down
1 change: 1 addition & 0 deletions internal/threagile/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
JsonTechnicalAssetsFilename = "technical-assets.json"
JsonStatsFilename = "stats.json"
TemplateFilename = "background.pdf"
ReportLogoImagePath = "report/threagile-logo.png"
DataFlowDiagramFilenameDOT = "data-flow-diagram.gv"
DataFlowDiagramFilenamePNG = "data-flow-diagram.png"
DataAssetDiagramFilenameDOT = "data-asset-diagram.gv"
Expand Down
4 changes: 4 additions & 0 deletions internal/threagile/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const (
skipRiskRulesFlagName = "skip-risk-rules"
ignoreOrphanedRiskTrackingFlagName = "ignore-orphaned-risk-tracking"
templateFileNameFlagName = "background"
reportLogoImagePathFlagName = "reportLogoImagePath"

generateDataFlowDiagramFlagName = "generate-data-flow-diagram"
generateDataAssetDiagramFlagName = "generate-data-asset-diagram"
Expand All @@ -36,6 +37,7 @@ const (
generateRisksExcelFlagName = "generate-risks-excel"
generateTagsExcelFlagName = "generate-tags-excel"
generateReportPDFFlagName = "generate-report-pdf"
generateReportADOCFlagName = "generate-report-adoc"
)

type Flags struct {
Expand All @@ -53,6 +55,7 @@ type Flags struct {
customRiskRulesPluginFlag string
ignoreOrphanedRiskTrackingFlag bool
templateFileNameFlag string
reportLogoImagePathFlag string
diagramDpiFlag int

generateDataFlowDiagramFlag bool
Expand All @@ -63,4 +66,5 @@ type Flags struct {
generateRisksExcelFlag bool
generateTagsExcelFlag bool
generateReportPDFFlag bool
generateReportADOCFlag bool
}
7 changes: 6 additions & 1 deletion internal/threagile/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func (what *Threagile) initRoot() *Threagile {
what.rootCmd.PersistentFlags().StringVar(&what.flags.skipRiskRulesFlag, skipRiskRulesFlagName, strings.Join(defaultConfig.SkipRiskRules, ","), "comma-separated list of risk rules (by their ID) to skip")
what.rootCmd.PersistentFlags().BoolVar(&what.flags.ignoreOrphanedRiskTrackingFlag, ignoreOrphanedRiskTrackingFlagName, defaultConfig.IgnoreOrphanedRiskTracking, "ignore orphaned risk tracking (just log them) not matching a concrete risk")
what.rootCmd.PersistentFlags().StringVar(&what.flags.templateFileNameFlag, templateFileNameFlagName, defaultConfig.TemplateFilename, "background pdf file")
what.rootCmd.PersistentFlags().StringVar(&what.flags.reportLogoImagePathFlag, reportLogoImagePathFlagName, defaultConfig.ReportLogoImagePath, "reportLogoImagePath")

what.rootCmd.PersistentFlags().BoolVar(&what.flags.generateDataFlowDiagramFlag, generateDataFlowDiagramFlagName, true, "generate data flow diagram")
what.rootCmd.PersistentFlags().BoolVar(&what.flags.generateDataAssetDiagramFlag, generateDataAssetDiagramFlagName, true, "generate data asset diagram")
Expand All @@ -80,7 +81,7 @@ func (what *Threagile) initRoot() *Threagile {
what.rootCmd.PersistentFlags().BoolVar(&what.flags.generateRisksExcelFlag, generateRisksExcelFlagName, true, "generate risks excel")
what.rootCmd.PersistentFlags().BoolVar(&what.flags.generateTagsExcelFlag, generateTagsExcelFlagName, true, "generate tags excel")
what.rootCmd.PersistentFlags().BoolVar(&what.flags.generateReportPDFFlag, generateReportPDFFlagName, true, "generate report pdf, including diagrams")

what.rootCmd.PersistentFlags().BoolVar(&what.flags.generateReportADOCFlag, generateReportADOCFlagName, true, "generate report adoc, including diagrams")
return what
}

Expand Down Expand Up @@ -214,6 +215,7 @@ func (what *Threagile) readCommands() *report.GenerateCommands {
commands.RisksExcel = what.flags.generateRisksExcelFlag
commands.TagsExcel = what.flags.generateTagsExcelFlag
commands.ReportPDF = what.flags.generateReportPDFFlag
commands.ReportADOC = what.flags.generateReportADOCFlag
return commands
}

Expand Down Expand Up @@ -265,6 +267,9 @@ func (what *Threagile) readConfig(cmd *cobra.Command, buildTimestamp string) *Co
if isFlagOverridden(flags, templateFileNameFlagName) {
cfg.TemplateFilename = what.flags.templateFileNameFlag
}
if isFlagOverridden(flags, reportLogoImagePathFlagName) {
cfg.ReportLogoImagePath = what.flags.reportLogoImagePathFlag
}
return cfg
}

Expand Down
Loading
Loading