Skip to content

Commit

Permalink
Merge pull request #41 from corazawaf/upgrade
Browse files Browse the repository at this point in the history
upgrade to the latest coraza v3 (v1.3.0 release)
  • Loading branch information
jptosso authored Jan 19, 2023
2 parents 7063a54 + 4e68df3 commit 54d0baa
Show file tree
Hide file tree
Showing 10 changed files with 396 additions and 956 deletions.
2 changes: 1 addition & 1 deletion caddy/main.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2022 The Corazawaf Authors.
// Copyright 2023 Juan Pablo Tosso and the OWASP Coraza contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down
91 changes: 38 additions & 53 deletions coraza.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2022 Juan Pablo Tosso and the OWASP Coraza contributors.
// Copyright 2023 Juan Pablo Tosso and the OWASP Coraza contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -15,7 +15,6 @@
package coraza

import (
"context"
"fmt"
"io"
"net/http"
Expand All @@ -27,45 +26,38 @@ import (
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/corazawaf/coraza/v3"
coraza_http "github.com/corazawaf/coraza/v3/http"
"github.com/corazawaf/coraza/v3/seclang"
"github.com/corazawaf/coraza/v3/types"
"go.uber.org/zap"
)

func init() {
caddy.RegisterModule(Coraza{})
caddy.RegisterModule(corazaModule{})
httpcaddyfile.RegisterHandlerDirective("coraza_waf", parseCaddyfile)
}

// Coraza is a Web Application Firewall implementation for Caddy.
type Coraza struct {
// corazaModule is a Web Application Firewall implementation for Caddy.
type corazaModule struct {
Include []string `json:"include"`
Directives string `json:"directives"`

logger *zap.Logger
waf *coraza.Waf
waf coraza.WAF
}

// CaddyModule returns the Caddy module information.
func (Coraza) CaddyModule() caddy.ModuleInfo {
func (corazaModule) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.handlers.waf",
New: func() caddy.Module { return new(Coraza) },
New: func() caddy.Module { return new(corazaModule) },
}
}

// Provision implements caddy.Provisioner.
func (m *Coraza) Provision(ctx caddy.Context) error {
var err error
func (m *corazaModule) Provision(ctx caddy.Context) error {
m.logger = ctx.Logger(m)
m.waf = coraza.NewWaf()
m.waf.SetErrorLogCb(logger(m.logger))
pp, _ := seclang.NewParser(m.waf)
config := coraza.NewWAFConfig().WithErrorCallback(logger(m.logger))
if m.Directives != "" {
if err = pp.FromString(m.Directives); err != nil {
return err
}
config = config.WithDirectives(m.Directives)
}
m.logger.Debug("Preparing to include files", zap.Int("count", len(m.Include)), zap.Strings("files", m.Include))
if len(m.Include) > 0 {
Expand All @@ -79,59 +71,52 @@ func (m *Coraza) Provision(ctx caddy.Context) error {
}
m.logger.Debug("Glob expanded", zap.String("pattern", file), zap.Strings("files", fs))
for _, f := range fs {
if err := pp.FromFile(f); err != nil {
return err
}
config = config.WithDirectivesFromFile(f)
}
} else {
m.logger.Debug("File was not a pattern, compiling it", zap.String("file", file))
if err := pp.FromFile(file); err != nil {
return err
}
config = config.WithDirectivesFromFile(file)
}
}
}
return nil
var err error
m.waf, err = coraza.NewWAF(config)
return err
}

// Validate implements caddy.Validator.
func (m *Coraza) Validate() error {
func (m *corazaModule) Validate() error {
return nil
}

// ServeHTTP implements caddyhttp.MiddlewareHandler.
func (m Coraza) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
func (m corazaModule) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
var err error
tx := m.waf.NewTransaction(context.Background())
id := randomString(16)
tx := m.waf.NewTransactionWithID(id)
defer func() {
tx.ProcessLogging()
_ = tx.Clean()
_ = tx.Close()
}()
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
repl.Set("http.transaction_id", tx.ID)
repl.Set("http.transaction_id", id)

it, err := coraza_http.ProcessRequest(tx, r)
it, err := processRequest(tx, r)
if err != nil {
return err
}
if it != nil {
return interrupt(nil, tx)
return interrupt(nil, tx, id)
}

// TODO this is a temporal fix while I fix it in coraza
re, err := tx.RequestBodyBuffer.Reader()
if err != nil {
return err
}
r.Body = io.NopCloser(re)
rec := newStreamRecorder(w, tx)
err = next.ServeHTTP(rec, r)
if err != nil {
return err
}
// If the response was interrupted during phase 3 or 4 we can stop the response
if tx.Interruption != nil {
return interrupt(nil, tx)
if tx.IsInterrupted() {
return interrupt(nil, tx, id)
}
if !rec.Buffered() {
//Nothing to do, response was already sent to the client
Expand All @@ -151,7 +136,7 @@ func (m Coraza) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp
}

// Unmarshal Caddyfile implements caddyfile.Unmarshaler.
func (m *Coraza) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
func (m *corazaModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
if !d.Next() {
return d.Err("expected token following filter")
}
Expand All @@ -177,15 +162,15 @@ func (m *Coraza) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {

// parseCaddyfile unmarshals tokens from h into a new Middleware.
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
var m Coraza
var m corazaModule
err := m.UnmarshalCaddyfile(h.Dispenser)
return m, err
}

func logger(logger *zap.Logger) coraza.ErrorLogCallback {
func logger(logger *zap.Logger) func(types.MatchedRule) {
return func(mr types.MatchedRule) {
data := mr.ErrorLog(403)
switch mr.Rule.Severity {
switch mr.Rule().Severity() {
case types.RuleSeverityEmergency:
logger.Error(data)
case types.RuleSeverityAlert:
Expand All @@ -206,29 +191,29 @@ func logger(logger *zap.Logger) coraza.ErrorLogCallback {
}
}

func interrupt(err error, tx *coraza.Transaction) error {
if tx.Interruption == nil {
func interrupt(err error, tx types.Transaction, id string) error {
if !tx.IsInterrupted() {
return caddyhttp.HandlerError{
StatusCode: 500,
ID: tx.ID,
ID: id,
Err: err,
}
}
status := tx.Interruption.Status
status := tx.Interruption().Status
if status <= 0 {
status = 403
}
return caddyhttp.HandlerError{
StatusCode: status,
ID: tx.ID,
ID: id,
Err: err,
}
}

// Interface guards
var (
_ caddy.Provisioner = (*Coraza)(nil)
_ caddy.Validator = (*Coraza)(nil)
_ caddyhttp.MiddlewareHandler = (*Coraza)(nil)
_ caddyfile.Unmarshaler = (*Coraza)(nil)
_ caddy.Provisioner = (*corazaModule)(nil)
_ caddy.Validator = (*corazaModule)(nil)
_ caddyhttp.MiddlewareHandler = (*corazaModule)(nil)
_ caddyfile.Unmarshaler = (*corazaModule)(nil)
)
22 changes: 11 additions & 11 deletions coraza_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2022 Juan Pablo Tosso and the OWASP Coraza contributors.
// Copyright 2023 Juan Pablo Tosso and the OWASP Coraza contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -32,21 +32,21 @@ import (
const baseURL = "http://127.0.0.1:8080"

func TestPlugin(t *testing.T) {
tester, err := newTester("test/caddyfile", t)
tester, err := newTester("test.init.config", t)
if err != nil {
t.Fatal(err)
}
tester.AssertGetResponse(baseURL+"/test", 200, "test123")
// if len(res.Header.Get("x-unique-id")) == 0 {
// t.Error("X-Unique-Id header is not set")
// }
res, _ := tester.AssertGetResponse(baseURL+"/test", 200, "test123")
if len(res.Header.Get("x-unique-id")) == 0 {
t.Error("X-Unique-Id header is not set")
}

time.Sleep(1 * time.Second)
}

func TestPluginReload(t *testing.T) {
tester := caddytest.NewTester(t)
configFile := "test/caddyfile"
configFile := "test.init.config"
configContent, err := ioutil.ReadFile(configFile)
if err != nil {
t.Fatalf("Failed to load configuration file %s: %s", configFile, err)
Expand All @@ -62,7 +62,7 @@ func TestPluginReload(t *testing.T) {
}

func TestSimpleRule(t *testing.T) {
tester, err := newTester("test/caddyfile", t)
tester, err := newTester("test.init.config", t)
if err != nil {
t.Fatal(err)
}
Expand All @@ -83,7 +83,7 @@ func TestSimpleRule(t *testing.T) {
}

func TestPhase3Disruption(t *testing.T) {
tester, err := newTester("test/caddyfile", t)
tester, err := newTester("test.init.config", t)
if err != nil {
t.Fatal(err)
}
Expand All @@ -94,7 +94,7 @@ func TestPhase3Disruption(t *testing.T) {
}

func TestPostUrlEncoded(t *testing.T) {
tester, err := newTester("test/caddyfile", t)
tester, err := newTester("test.init.config", t)
if err != nil {
t.Fatal(err)
}
Expand All @@ -107,7 +107,7 @@ func TestPostUrlEncoded(t *testing.T) {
}

func TestPostMultipart(t *testing.T) {
tester, err := newTester("test/caddyfile", t)
tester, err := newTester("test.init.config", t)
if err != nil {
t.Fatal(err)
}
Expand Down
Loading

0 comments on commit 54d0baa

Please sign in to comment.