From 7a3bf8fbeee5342acb73218770570ceacf41f8e5 Mon Sep 17 00:00:00 2001
From: Pake
Date: Thu, 5 Jun 2025 14:15:16 +0200
Subject: [PATCH 001/186] Added Makefile, Github Actions and test template
---
.github/workflows/main.yml | 43 ++++++++++++++++++++++++++++++++++++++
Makefile | 36 +++++++++++++++++++++++++++++++
usecases/test_sample.go | 33 +++++++++++++++++++++++++++++
3 files changed, 112 insertions(+)
create mode 100644 .github/workflows/main.yml
create mode 100644 Makefile
create mode 100644 usecases/test_sample.go
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..4cd3683
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,43 @@
+name: Linters, Spellcheck, and Tests
+
+on:
+ push:
+ workflow_dispatch:
+
+jobs:
+ Linters:
+ runs-on: ubuntu-latest
+ timeout-minutes: 2
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup go
+ uses: actions/setup-go@v5
+ with:
+ go-version: 1.23
+ - name: Install dependencies
+ run: make deps
+ - name: Run linters
+ run: make lint
+
+ Spellcheck:
+ runs-on: ubuntu-latest
+ timeout-minutes: 2
+ steps:
+ - uses: actions/checkout@v4
+ - uses: crate-ci/typos@v1.29.7
+
+ Tests:
+ runs-on: ubuntu-latest
+ timeout-minutes: 2
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup go
+ uses: actions/setup-go@v5
+ with:
+ go-version: 1.23
+ - name: Install dependencies
+ run: make deps
+ - name: Run tests
+ run: make test
+ - name: Report stats
+ run: make analyse
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d7ce277
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,36 @@
+# Run tests and log the test coverage
+test:
+ go test -v -coverprofile=".cover.out" $$(go list ./... | grep -v /tmp)
+
+# Runs source code linters and catches common errors
+lint:
+ test -z $$(gofmt -l .) || (echo "Code isn't gofmt'ed!" && exit 1)
+ go vet $$(go list ./... | grep -v /tmp)
+ gosec -quiet -fmt=golint -exclude-dir="tmp" ./...
+ pointerinterface ./...
+
+# Runs spellchecker on the code and comments
+# This requires this tool to be installed from https://github.com/crate-ci/typos?tab=readme-ov-file
+# Example installation (if you have rust installed): cargo install typos-cli
+spellcheck:
+ typos .
+
+# Generate pretty coverage report
+analyse:
+ go tool cover -html=".cover.out" -o="cover.html"
+ @echo -e "\nCOVERAGE\n===================="
+ go tool cover -func=.cover.out
+ @echo -e "\nCYCLOMATIC COMPLEXITY\n===================="
+ gocyclo -avg -top 10 -ignore test.go .
+
+# Updates 3rd party packages and tools
+deps:
+ go mod download
+ go install github.com/securego/gosec/v2/cmd/gosec@latest
+ go install github.com/fzipp/gocyclo/cmd/gocyclo@latest
+ go install code.larus.se/lmas/pointerinterface@latest
+
+# Clean up built binary and other temporary files (ignores errors from rm)
+clean:
+ go clean
+ rm .cover.out cover.html
diff --git a/usecases/test_sample.go b/usecases/test_sample.go
new file mode 100644
index 0000000..a7dfecd
--- /dev/null
+++ b/usecases/test_sample.go
@@ -0,0 +1,33 @@
+package usecases
+
+import (
+ "net/http"
+ "testing"
+)
+
+// mockTransport is used for replacing the default network Transport (used by
+// http.DefaultClient) and it will intercept network requests.
+type mockTransport struct {
+ resp *http.Response
+}
+
+func newMockTransport(resp *http.Response) mockTransport {
+ t := mockTransport{
+ resp: resp,
+ }
+ // Hijack the default http client so no actual http requests are sent over the network
+ http.DefaultClient.Transport = t
+ return t
+}
+
+// RoundTrip method is required to fulfil the RoundTripper interface (as required by the DefaultClient).
+// It prevents the request from being sent over the network and count how many times
+// a domain was requested.
+func (t mockTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
+ t.resp.Request = req
+ return t.resp, nil
+}
+
+func sample(t *testing.T) {
+ return
+}
From 1a0bf9e3500e4a37ad05a1b31ff556e0522ea654 Mon Sep 17 00:00:00 2001
From: Pake
Date: Thu, 5 Jun 2025 14:17:59 +0200
Subject: [PATCH 002/186] Making sure tests run
---
usecases/test_sample.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/usecases/test_sample.go b/usecases/test_sample.go
index a7dfecd..14ff4fb 100644
--- a/usecases/test_sample.go
+++ b/usecases/test_sample.go
@@ -28,6 +28,6 @@ func (t mockTransport) RoundTrip(req *http.Request) (resp *http.Response, err er
return t.resp, nil
}
-func sample(t *testing.T) {
+func test_sample(t *testing.T) {
return
}
From 9bac8c374977cf10408bf8045ba60992a07c19c5 Mon Sep 17 00:00:00 2001
From: Pake
Date: Thu, 5 Jun 2025 16:34:30 +0200
Subject: [PATCH 003/186] Fixed linter errors by adding error handlers,
MinVersion for tls and read/write timeouts in server config
---
usecases/authentication.go | 22 ++++++++--
usecases/cost.go | 5 ++-
usecases/docs.go | 81 ++++++++++++++++--------------------
usecases/provision.go | 12 +++++-
usecases/serversNhandlers.go | 25 +++++++----
5 files changed, 87 insertions(+), 58 deletions(-)
diff --git a/usecases/authentication.go b/usecases/authentication.go
index 134ff25..8138fd9 100644
--- a/usecases/authentication.go
+++ b/usecases/authentication.go
@@ -93,6 +93,7 @@ func RequestCertificate(sys *components.System) {
Certificates: []tls.Certificate{clientCert},
RootCAs: caCertPool,
InsecureSkipVerify: false,
+ MinVersion: tls.VersionTLS12,
}
sys.Husk.TlsConfig = tlsConfig
@@ -137,7 +138,11 @@ func sendCSR(sys *components.System, csrPEM []byte) (string, error) {
// Read the response body
buf := new(bytes.Buffer)
- buf.ReadFrom(resp.Body)
+ _, err = buf.ReadFrom(resp.Body)
+ if err != nil {
+ log.Printf("Error while reading body: %v", err)
+ return "", err
+ }
return buf.String(), nil
}
@@ -158,7 +163,14 @@ func getCACertificate(sys *components.System) (string, error) {
url := strings.TrimSuffix(coreUAurl, "ification") // the configuration file address to the CA includes the unit asset
// Make a GET request to the CA's endpoint
- resp, err := http.Get(url)
+ // https://stackoverflow.com/questions/70281883/golang-untaint-url-variable-to-fix-gosec-warning-g107
+ //resp, err := http.Get(url)
+ req, err := http.NewRequest(http.MethodGet, url, nil)
+ if err != nil {
+ log.Printf("Error creating NewRequest: %v", err)
+ return "", err
+ }
+ resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", fmt.Errorf("failed to send request to CA: %w", err)
}
@@ -171,7 +183,11 @@ func getCACertificate(sys *components.System) (string, error) {
// Read the response body
buf := new(bytes.Buffer)
- buf.ReadFrom(resp.Body)
+ _, err = buf.ReadFrom(resp.Body)
+ if err != nil {
+ log.Printf("Error while reading body: %v", err)
+ return "", err
+ }
return buf.String(), nil
}
diff --git a/usecases/cost.go b/usecases/cost.go
index 1a3f1a2..888e8d5 100644
--- a/usecases/cost.go
+++ b/usecases/cost.go
@@ -94,7 +94,10 @@ func ACServices(w http.ResponseWriter, r *http.Request, ua *components.UnitAsset
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
- w.Write(payload)
+ _, err = w.Write(payload)
+ if err != nil {
+ log.Printf("Error while writing to response body for ACServices: %v", err)
+ }
return
case "PUT":
defer r.Body.Close()
diff --git a/usecases/docs.go b/usecases/docs.go
index 412b542..f2644a0 100644
--- a/usecases/docs.go
+++ b/usecases/docs.go
@@ -29,6 +29,7 @@ package usecases
import (
"fmt"
+ "log"
"net/http"
"strconv"
"strings"
@@ -39,102 +40,92 @@ import (
// System Documentation (based on HATEOAS) provides an initial documentation on the system's web server of with hyperlinks to the services for browsers
// HATEOAS is the acronym for Hypermedia as the Engine of Application State, using hyperlinks to navigate the API
func SysHateoas(w http.ResponseWriter, req *http.Request, sys components.System) {
- text := ""
- w.Write([]byte(text))
- text = "System Description
"
- w.Write([]byte(text))
- text = "The system " + sys.Name + " " + sys.Husk.Description + "
"
- w.Write([]byte(text))
- text = "Online Documentation
"
- w.Write([]byte(text))
-
- text = " The resource list is
"
- w.Write([]byte(text))
+ text := "\n"
+ text += "System Description
\n"
+ text += "The system " + sys.Name + " " + sys.Husk.Description + "
\n"
+ text += "Online Documentation\n"
+ text += " The resource list is
\n"
+
assetList := &sys.UAssets
for _, unitasset := range *assetList {
metaservice := ""
for key, values := range (*unitasset).GetDetails() {
metaservice += key + ": " + fmt.Sprintf("%v", values) + " "
}
- resourceURI := "- " + (*unitasset).GetName() + " with details " + metaservice + "
"
- w.Write([]byte(resourceURI))
+ text += "- " + (*unitasset).GetName() + " with details " + metaservice + "
\n"
}
- text = "
having the following services:"
- w.Write([]byte(text))
+ text += "
having the following services:\n"
servicesList := getServicesList(getFirstAsset(*assetList)[0])
for _, service := range servicesList {
metaservice := ""
for key, values := range service.Details {
metaservice += key + ": " + fmt.Sprintf("%v", values) + " "
}
- serviceURI := "- " + service.Definition + " with details: " + metaservice + "
"
- w.Write([]byte(serviceURI))
+ text += "- " + service.Definition + " with details: " + metaservice + "
\n"
}
- text = "
The services can be accessed using the following protocols with their respective bound ports:
"
- w.Write([]byte(text))
+ text += "
The services can be accessed using the following protocols with their respective bound ports:
\n"
for protocol, port := range sys.Husk.ProtoPort {
- protoDoor := "- Protocol " + protocol + " using port " + strconv.Itoa(port) + "
"
- w.Write([]byte(protoDoor))
+ text += "- Protocol " + protocol + " using port " + strconv.Itoa(port) + "
\n"
}
- text = "
of the device whose IP addresses are (upon startup):
"
- w.Write([]byte(text))
+ text += "
of the device whose IP addresses are (upon startup):
\n"
for _, IPAddre := range sys.Host.IPAddresses {
- hostaddresses := "- " + IPAddre + "
"
- w.Write([]byte(hostaddresses))
+ text += "- " + IPAddre + "
\n"
}
- text = "
"
- w.Write([]byte(text))
+ text += "
"
+ _, err := w.Write([]byte(text))
+ if err != nil {
+ log.Printf("Error while writing to response body for SysHateoas: %v", err)
+ }
}
// ResHateoas provides information about the unit asset(s) and each service and is accessed via the system's web server
func ResHateoas(w http.ResponseWriter, req *http.Request, ua components.UnitAsset, sys components.System) {
- text := ""
- w.Write([]byte(text))
-
- text = "Unit Asset Description
"
- w.Write([]byte(text))
+ text := "\n"
+ text += "Unit Asset Description
\n"
uaName := ua.GetName()
metaservice := ""
for key, values := range ua.GetDetails() {
metaservice += key + ": " + fmt.Sprintf("%v", values) + " "
}
- text = "The resource " + uaName + " belongs to system " + sys.Name + " and has the details " + metaservice + " with the following services:" + ""
- w.Write([]byte(text))
+ text += "The resource " + uaName + " belongs to system " + sys.Name + " and has the details " + metaservice + " with the following services:" + "\n"
+
services := ua.GetServices()
for _, service := range services {
metaservice := ""
for key, values := range service.Details {
metaservice += key + ": " + fmt.Sprintf("%v", values) + " "
}
- serviceURI := "- " + service.Definition + " with details: " + metaservice + "
"
- w.Write([]byte(serviceURI))
+ text += "- " + service.Definition + " with details: " + metaservice + "
\n"
}
- text = "
"
- w.Write([]byte(text))
+ text += "