Commit 9f203043 authored by Rémi PAILHAREY's avatar Rémi PAILHAREY
Browse files

feat: handle timeouts + send error emails to user

parent b8ae9157
......@@ -11,7 +11,7 @@
"mode": "debug",
"program": "${workspaceFolder}",
"env": {
"LOG_FILE": "./log.txt",
// "LOG_FILE": "./log.txt",
"ADMIN_ROLE": "ADMINS",
"USER_ROLE": "USERS",
"INMEMORY_TOKEN_LIFE_DAYS": "2",
......
{
"Token": "MpaxfF-x3iewHcbvlfulNkYI1oJs2a28MFTNhBz5_iM="
}
\ No newline at end of file
......@@ -13,6 +13,7 @@ import (
"os"
"regexp"
"strings"
"time"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/common"
)
......@@ -35,7 +36,7 @@ func Init() {
// Uploads a file to Cuckoo and requests an analysis
func SendPostRequestMultipart(filename string) int {
url := cuckooURL + "/tasks/create/file"
client := &http.Client{}
client := &http.Client{Timeout: time.Second * 30}
req, err := NewfileUploadRequest(url, nil, "file", filename)
if err != nil {
log.Fatal(err)
......@@ -65,7 +66,7 @@ func SendPostRequestMultipart(filename string) int {
func SendGetSummaryReport(taskid int) (report []byte, err error) {
url := fmt.Sprintf("%s/tasks/summary/%d", cuckooURL, taskid)
client := &http.Client{}
client := &http.Client{Timeout: time.Second * 10}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, errors.New("Report couldn't be fetched (request error)")
......
......@@ -8,6 +8,7 @@ import (
"io/ioutil"
"net/http"
"os"
"strings"
"time"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/common"
......@@ -20,6 +21,7 @@ import (
var (
CybersignalAuthToken string
ackMailTemplate = "templates/ackmailtemplate.html"
oopsMailTemplate = "templates/oopsmailtemplate.html"
resultMailTemplate = "templates/resultmailtemplate.html"
ssiMailTemplate = "templates/ssimailtemplate.html"
mailConfig = mail.EmailConfig{
......@@ -104,13 +106,17 @@ func HandleUpload() http.Handler {
report, err = cuckoo.SendGetSummaryReport(taskid)
if err == nil {
break
} else if strings.Contains(err.Error(), "use of closed network connection") {
log.Logger.Println("error timeout : cuckoo.SendGetSummaryReport()")
}
time.Sleep(30 * time.Second)
}
severity, senderEmail, subject, urls, sha1, md5, err := cuckoo.ExtractIOC(report)
if err != nil {
log.Logger.Fatal(err)
log.Logger.Println(err)
sendOopsMail(userMail)
return
}
log.Logger.Printf("| Got results of report ID : %d | Severity : %d | %v | %v", taskid, severity, r.RemoteAddr, log.GetCityAndCountryFromRequest(r))
......@@ -122,29 +128,63 @@ func HandleUpload() http.Handler {
tags := []string{"Cyber-Signal"}
caseCreated, err := thehive.SendPostCreateCase(subject, "Test Cyber-Signal", thehive.RedTlp, severity, nil, tags, false, thehive.MetricField{})
if err != nil {
log.Logger.Fatal(err)
log.Logger.Println(err)
sendOopsMail(userMail)
return
}
// Add all observables
thehive.SendPostAddObservable(caseCreated.Id, senderEmail, "email-src", "L'adresse mail de l'expéditeur", thehive.RedTlp, tags, true)
thehive.SendPostAddObservable(caseCreated.Id, subject, "mail-subject", "L'objet du mail signalé", thehive.RedTlp, tags, true)
thehive.SendPostAddObservable(caseCreated.Id, sha1, "sha1", "Le hash sha1 du mail signalé", thehive.RedTlp, tags, true)
thehive.SendPostAddObservable(caseCreated.Id, md5, "md5", "Le hash md5 du mail signalé", thehive.RedTlp, tags, true)
_, err = thehive.SendPostAddObservable(caseCreated.Id, senderEmail, "email-src", "L'adresse mail de l'expéditeur", thehive.RedTlp, tags, true)
if err != nil {
log.Logger.Println(err)
sendOopsMail(userMail)
return
}
_, err = thehive.SendPostAddObservable(caseCreated.Id, subject, "mail-subject", "L'objet du mail signalé", thehive.RedTlp, tags, true)
if err != nil {
log.Logger.Println(err)
sendOopsMail(userMail)
return
}
_, err = thehive.SendPostAddObservable(caseCreated.Id, sha1, "sha1", "Le hash sha1 du mail signalé", thehive.RedTlp, tags, true)
if err != nil {
log.Logger.Println(err)
sendOopsMail(userMail)
return
}
_, err = thehive.SendPostAddObservable(caseCreated.Id, md5, "md5", "Le hash md5 du mail signalé", thehive.RedTlp, tags, true)
if err != nil {
log.Logger.Println(err)
sendOopsMail(userMail)
return
}
for i := range urls {
thehive.SendPostAddObservable(caseCreated.Id, urls[i], "url", "Un lien présent dans le mail signalé", thehive.RedTlp, tags, true)
_, err = thehive.SendPostAddObservable(caseCreated.Id, urls[i], "url", "Un lien présent dans le mail signalé", thehive.RedTlp, tags, true)
if err != nil {
log.Logger.Println(err)
sendOopsMail(userMail)
return
}
}
// Find linked cases
linkedCases, err := thehive.SendGetLinkedCases(caseCreated.Id)
if err != nil {
log.Logger.Fatal(err)
log.Logger.Println(err)
sendOopsMail(userMail)
return
}
// If there are linked cases, update their metric field
if len(linkedCases) != 0 {
for i := range linkedCases {
linkedCase := linkedCases[i]
thehive.SendPatchIncrementMetric(linkedCase)
err = thehive.SendPatchIncrementMetric(linkedCase)
if err != nil {
log.Logger.Println(err)
sendOopsMail(userMail)
return
}
}
log.Logger.Printf("| Incident created with similar cases | Case ID : %v | %v | %v", caseCreated.Id, r.RemoteAddr, log.GetCityAndCountryFromRequest(r))
} else {
......@@ -192,6 +232,25 @@ func sendAcknowledgmentMail(email string) error {
return mailSender.Send(to, body.Bytes())
}
func sendOopsMail(email string) error {
// Receiver email address.
to := []string{
email,
}
t, _ := template.ParseFiles(oopsMailTemplate)
var body bytes.Buffer
mimeHeaders := "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n"
body.Write([]byte(fmt.Sprintf("Subject: Accusé de réception de votre signalement \n%s\n\n", mimeHeaders)))
t.Execute(&body, struct{}{})
// Sending email.
return mailSender.Send(to, body.Bytes())
}
func sendResultMail(email string, severity int) error {
// Receiver email address.
to := []string{
......@@ -270,6 +329,7 @@ func sendSSIMail(caseId string, severity int) error {
// SetTestMailSender allows overriding mail sender for test purposes
func SetTestModeAndReturnRecorder() *mail.EmailRecorder {
ackMailTemplate = "../../templates/ackmailtemplate.html"
oopsMailTemplate = "../../templates/oopsmailtemplate.html"
resultMailTemplate = "../../templates/resultmailtemplate.html"
ssiMailTemplate = "../../templates/ssimailtemplate.html"
sender, recorder := mail.NewMockSender()
......
......@@ -8,7 +8,6 @@ func TestEmail_SendSuccessful(t *testing.T) {
sender, recorder := NewMockSender()
body := "Hello World"
err := sender.Send([]string{"me@example.com"}, []byte(body))
if err != nil {
t.Errorf("unexpected error: %s", err)
}
......
......@@ -53,7 +53,8 @@ func CreateMockTheHiveAPI() *http.ServeMux {
// POST Create new observable
mux.HandleFunc("/api/case/1/artifact", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{
w.WriteHeader(http.StatusCreated)
w.Write([]byte(`[{
"id":"123456",
"createdBy":"rpailharey@grandlyon.com",
"createdAt":1615283443807,
......@@ -66,7 +67,7 @@ func CreateMockTheHiveAPI() *http.ServeMux {
"ioc":true,
"sighted":true,
"message":"Ceci est un observable de test"
}`))
}]`))
})
// GET Get linked cases
......
......@@ -30,7 +30,7 @@ var (
func init() {
tokens.Init("../../configs/tokenskey.json", true)
cybersignal.Init("../../authtoken.json")
cybersignal.Init("../../configs/authtoken.json")
authorizationHeader = make(map[string]string)
authorizationHeader["Authorization"] = "Bearer " + cybersignal.CybersignalAuthToken
}
......
......@@ -8,6 +8,7 @@ import (
"io/ioutil"
"log"
"net/http"
"time"
"forge.grandlyon.com/rpailharey/cyber-signal/internal/common"
)
......@@ -181,7 +182,7 @@ func SendPostCreateCase(title string, description string, tlp int, severity int,
}
requestBody := bytes.NewReader(jsondata)
client := &http.Client{}
client := &http.Client{Timeout: time.Second * 10}
req, err := http.NewRequest("POST", url, requestBody)
if err != nil {
return *new(CaseResp), err
......@@ -215,7 +216,7 @@ func SendPostFindCase(query []byte) ([]CaseResp, error) {
requestBody := bytes.NewReader(query)
client := &http.Client{}
client := &http.Client{Timeout: time.Second * 10}
req, err := http.NewRequest("POST", url, requestBody)
if err != nil {
return nil, err
......@@ -256,7 +257,7 @@ func SendGetCyberSignalCases(w http.ResponseWriter, req *http.Request) {
func SendGetFindCase(caseId string) (CaseResp, error) {
url := thehiveURL + "/api/case/" + caseId
client := &http.Client{}
client := &http.Client{Timeout: time.Second * 10}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return *new(CaseResp), err
......@@ -280,7 +281,7 @@ func SendGetFindCase(caseId string) (CaseResp, error) {
func SendGetLinkedCases(caseId string) ([]LinkedCase, error) {
url := thehiveURL + "/api/case/" + caseId + "/links"
client := &http.Client{}
client := &http.Client{Timeout: time.Second * 10}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
......@@ -306,7 +307,7 @@ func SendGetLinkedCases(caseId string) ([]LinkedCase, error) {
func SendPatchIncrementMetric(linkedCase LinkedCase) error {
url := thehiveURL + "/api/case/" + linkedCase.Id
client := &http.Client{}
client := &http.Client{Timeout: time.Second * 10}
var updatedFields struct {
CustomFields MetricField `json:"customFields"`
......@@ -343,7 +344,7 @@ func SendPatchIncrementMetric(linkedCase LinkedCase) error {
func SendDeleteCase(caseId string) error {
url := thehiveURL + "/api/case/" + caseId + "/force"
client := &http.Client{}
client := &http.Client{Timeout: time.Second * 10}
req, err := http.NewRequest("DELETE", url, nil)
if err != nil {
return err
......@@ -380,7 +381,7 @@ func SendPostAddObservable(caseId string, data string, dataType string, message
}
requestBody := bytes.NewReader(jsondata)
client := &http.Client{}
client := &http.Client{Timeout: time.Second * 10}
req, err := http.NewRequest("POST", url, requestBody)
if err != nil {
return ObservableResp{}, err
......
<!DOCTYPE html>
<style>
body {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS",
sans-serif;
}
header {
display: flex;
align-items: center;
justify-content: center;
background-color: #e7e7e7;
padding: 10px;
}
</style>
<html>
<body>
<header>
<img
src="https://www.grandlyon.com/typo3conf/ext/gl_metropole_template/Resources/Public/Images/Content/logo-grand-lyon-la-metropole.png"
alt="Main logo"
/>
</header>
<h1>Erreur pendant l'analyse</h1>
<p>Bonjour,</p>
<p>
Vous avez récemment signalé un mail et une erreur inattendue est survenue
pendant son traitement !
</p>
<p>
Nous en avons informé l'équipe SSI, qui fera son possible pour résoudre le
problème au plus vite. Nous vous invitons à réessayer plus tard.
</p>
<p>Merci pour votre compréhension et à bientôt !</p>
</body>
</html>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment