{"id":36464934,"url":"https://github.com/sitebatch/waffle-go","last_synced_at":"2026-01-12T00:01:49.308Z","repository":{"id":270198861,"uuid":"909612935","full_name":"sitebatch/waffle-go","owner":"sitebatch","description":"Waffle is a library for integrating a Web Application Firewall (WAF) into Go applications.","archived":false,"fork":false,"pushed_at":"2025-12-18T22:37:12.000Z","size":731,"stargazers_count":18,"open_issues_count":19,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-19T01:56:38.745Z","etag":null,"topics":["golang","security","waf"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sitebatch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-12-29T08:52:21.000Z","updated_at":"2025-10-31T07:21:03.000Z","dependencies_parsed_at":"2024-12-29T10:18:16.364Z","dependency_job_id":"6abd3b5b-40eb-4227-9b3a-50d1917c4ded","html_url":"https://github.com/sitebatch/waffle-go","commit_stats":null,"previous_names":["sitebatch/waffle-go"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/sitebatch/waffle-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sitebatch%2Fwaffle-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sitebatch%2Fwaffle-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sitebatch%2Fwaffle-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sitebatch%2Fwaffle-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sitebatch","download_url":"https://codeload.github.com/sitebatch/waffle-go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sitebatch%2Fwaffle-go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28328676,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-11T22:11:01.104Z","status":"ssl_error","status_checked_at":"2026-01-11T22:10:58.990Z","response_time":60,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","security","waf"],"created_at":"2026-01-12T00:01:49.224Z","updated_at":"2026-01-12T00:01:49.292Z","avatar_url":"https://github.com/sitebatch.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# waffle-go\n\nWaffle is a library for integrating a Web Application Firewall (WAF) into Go applications.\n\nBy embedding the WAF directly within the application rather than at the network boundary, you can achieve more accurate and flexible detection and defense against attacks.\n\n## Features\n\n- Integration with minimal code changes\n- Protection against common web attacks including XSS, SQL injection, and SSRF\n- Protection against business logic vulnerabilities like Account Takeover\n- Support for popular Go web frameworks and libraries\n\n## Use Cases\n\n- Protecting web applications and APIs from common web attacks\n- Alternative to traditional network-based WAFs for application-level protection\n- Enhanced security for applications using database access and file operations\n\n## Getting Started\n\nFirst, set up the Waffle library.\n\n```bash\ngo get github.com/sitebatch/waffle-go\n```\n\n```go\npackage main\n\nimport (\n    \"net/http\"\n    \"github.com/sitebatch/waffle-go\"\n)\n\nfunc main() {\n    // Start Waffle\n    if err := waffle.Start(); err != nil {\n        // handle error\n    }\n}\n```\n\nFinally, depending on which libraries your application uses, install the Waffle contrib package and apply the middleware or wrapper function.  \nThe following libraries are supported:\n\n| Library      | Contrib Package                                                |\n| :----------- | :------------------------------------------------------------- |\n| Gin          | [contrib/gin-gonic/gin](contrib/gin-gonic/gin/README.md)       |\n| Echo         | [contrib/labstack/echo](contrib/labstack/echo/README.md)       |\n| net/http     | [contrib/net/http](contrib/net/http/README.md)                 |\n| gqlgen       | [contrib/99designs/gqlgen](contrib/99designs/gqlgen/README.md) |\n| database/sql | [contrib/database/sql](contrib/database/sql/README.md)         |\n| os           | [contrib/os](contrib/os/README.md)                             |\n\n### Example\n\nThe following example uses a basic `net/http` application.\nFull example code can be found in the [examples/auth](./examples/auth/).\n\nThis application only provides authentication functionality via the /login endpoint.\nThe `login()` function executed during login is vulnerable to SQL injection.\n\n\u003cdetails\u003e\u003csummary\u003eClick to expand code\u003c/summary\u003e\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/signal\"\n\t\"time\"\n\n\t_ \"github.com/mattn/go-sqlite3\"\n)\n\nvar database *sql.DB\n\nfunc init() {\n\tif err := setupDB(); err != nil {\n\t\tlog.Fatalf(\"failed to setup database: %v\", err)\n\t}\n}\n\nfunc main() {\n\tctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)\n\tdefer stop()\n\n\tsrv := \u0026http.Server{\n\t\tAddr:         \":8080\",\n\t\tReadTimeout:  time.Second,\n\t\tWriteTimeout: 10 * time.Second,\n\t\tHandler:      newHTTPHandler(),\n\t}\n\n\tlog.Printf(\"starting server at %s\", srv.Addr)\n\n\tsrvErr := make(chan error, 1)\n\tgo func() {\n\t\tsrvErr \u003c- srv.ListenAndServe()\n\t}()\n\n\tselect {\n\tcase err := \u003c-srvErr:\n\t\tlog.Fatal(err)\n\tcase \u003c-ctx.Done():\n\t\tstop()\n\t}\n\n\tif err := srv.Shutdown(context.Background()); err != nil {\n\t\tlog.Fatalf(\"server shutdown failed: %v\", err)\n\t}\n}\n\nfunc loginHandler(w http.ResponseWriter, r *http.Request) {\n\temail := r.FormValue(\"email\")\n\tpassword := r.FormValue(\"password\")\n\n\tif err := login(r.Context(), email, password); err != nil {\n\t\thttp.Error(w, \"invalid credentials\", http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\tfmt.Fprintln(w, \"login successful\")\n}\n\nfunc login(ctx context.Context, email, password string) error {\n    // Vulnerable to SQL injection\n\trows, err := database.QueryContext(ctx, fmt.Sprintf(\n\t\t\"SELECT * FROM users WHERE email = '%s' AND password = '%s';\", email, password,\n\t))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdefer rows.Close()\n\n\tif !rows.Next() {\n\t\treturn fmt.Errorf(\"invalid credentials\")\n\t}\n\n\treturn nil\n}\n\nfunc newHTTPHandler() http.Handler {\n\tmux := http.NewServeMux()\n\n\tmux.HandleFunc(\"/login\", func(w http.ResponseWriter, r *http.Request) {\n\t\tloginHandler(w, r)\n\t})\n\n\treturn mux\n}\n\nfunc setupDB() error {\n\tdb, err := sql.Open(\"sqlite3\", \"file::memory:?cache=shared\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif _, err := db.Exec(\"CREATE TABLE users(id int, email text, password text);\"); err != nil {\n\t\treturn err\n\t}\n\n\tif _, err := db.Exec(\"INSERT INTO users(id, email, password) VALUES(1, 'user@example.com', 'password');\"); err != nil {\n\t\treturn err\n\t}\n\n\tdatabase = db\n\n\treturn nil\n}\n```\n\n\u003c/details\u003e\n\n### Initialize Waffle\n\nFirst, initialize Waffle at the start of your application.\n\n```go\nfunc main() {\n    ...\n    // SetBlockResponseTemplateHTML sets the response body returned when a request is blocked.\n    // Here, we set it to \"request blocked\".\n\twaffle.SetBlockResponseTemplateHTML([]byte(\"request blocked\"))\n    // SetExporter sets the exporter to retrieve detection events.\n    // Here, we use the built-in StdoutExporter to log events to stdout.\n\twaffle.SetExporter(exporter.NewStdoutExporter())\n\n    // Start Waffle\n\tif err := waffle.Start(); err != nil {\n\t\tlog.Fatalf(\"failed to start waffle: %v\", err)\n\t}\n    ...\n}\n```\n\n### Apply Middleware to HTTP Handlers\n\nNext, wrap your HTTP handlers with Waffle's HTTP middleware.\nBy adding this middleware, Waffle can monitor HTTP requests to detect and block suspicious payloads.\n\n```go\nimport (\n    ...\n\twaffleHttp \"github.com/sitebatch/waffle-go/contrib/net/http\"\n    ...\n)\n\nfunc newHTTPHandler() http.Handler {\n\tmux := http.NewServeMux()\n\n\tmux.HandleFunc(\"/login\", func(w http.ResponseWriter, r *http.Request) {\n\t\tloginHandler(w, r)\n\t})\n\n    // Wrap the mux with Waffle's HTTP middleware\n\thandler := waffleHttp.WafMiddleware(mux)\n\treturn handler\n}\n```\n\nAt this point, Waffle can inspect HTTP requests to detect SQL injection attempts.\n\n```shell\n$ curl -X POST 'http://localhost:8080/login' --data \"email=user@example.com' OR 1=1--\u0026password=password\"\nlogin successful\n\n# Waffle's exporter log output:\n2025/10/31 15:46:45 logger.go:41: \"msg\"=\"\" \"error\"=\"detected sqli payload: SQLi detected\" \"detected_at\"=\"2025-10-31 15:46:45.809583 +0900 JST m=+13.611103459\" \"request_url\"=\"http://localhost:8080/login\" \"rule_id\"=\"sql-injection-attempts\" \"block\"=false \"meta\"={}\n```\n\nHowever, this only detects payloads that \"look like\" SQL injections.\nThis leads to false positives, where a detection occurs even if there is no actual SQL injection vulnerability in the code.\n\n### Apply SQL Wrapper to Database Access\n\nWaffle provides a SQL wrapper that can monitor SQL queries executed via the `database/sql` package.\nBy wrapping database connections with this wrapper, Waffle can accurately detect and block actual SQL injection attempts.\n\n```go\nimport (\n    ...\n    waffleSQL \"github.com/sitebatch/waffle-go/contrib/database/sql\"\n    ...\n)\n\nfunc loginHandler(w http.ResponseWriter, r *http.Request) {\n\temail := r.FormValue(\"email\")\n\tpassword := r.FormValue(\"password\")\n\n\tif err := login(r.Context(), email, password); err != nil {\n        // Check if the error is a security blocking error\n\t\tif waf.IsSecurityBlockingError(err) {\n\t\t\treturn\n\t\t}\n\n\t\thttp.Error(w, \"invalid credentials\", http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\tfmt.Fprintln(w, \"login successful\")\n}\n\nfunc setupDB() error {\n    // Wrap the database connection with Waffle's SQL wrapper\n\tdb, err := waffleSQL.Open(\"sqlite3\", \"file::memory:?cache=shared\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n    ...\n\n\tdatabase = db\n\n\treturn nil\n}\n```\n\nWhen a request is sent that triggers SQL injection, Waffle inspects the SQL that will be executed (not the HTTP request) and blocks it.\n\n```shell\n$ curl -X POST 'http://localhost:8080/login' --data \"email=user@example.com' OR 1=1--\u0026password=password\"\nrequest blocked\n\n# Waffle's exporter log output:\n2025/10/31 16:15:19 logger.go:41: \"msg\"=\"\" \"error\"=\"detected sql injection, because of where tautology\" \"detected_at\"=\"2025-10-31 16:15:19.901821 +0900 JST m=+1081.473456834\" \"request_url\"=\"http://localhost:8080/login\" \"rule_id\"=\"sql-injection-exploited\" \"block\"=true \"meta\"={}\n```\n\n### Next Steps\n\nWaffle also provides protection against other attack vectors, such as directory traversal, SSRF, and more.\nFor more details, please refer to the README of each [contrib package](./contrib/).\n\n## Configuration\n\n### Custom Rules\n\nYou can provide custom WAF rules:\n\n```go\nwaffle.Start(waffle.WithRule(customRuleJSON))\n```\n\n### Error Handling\n\nSet a custom handler to handle Waffle's internal errors.\n\n```go\nwaffle.SetErrorHandler(customErrorHandler)\n```\n\n### Event Export\n\nTo retrieve events detected by Waffle, configure an exporter using `SetExporter()`.\n\nWaffle provides built-in exporters like `StdoutExporter` for logging detection events and `ChanExporter` for writing to a specified channel, but you can also implement and configure your own custom exporter that meets the required interface.\n\n```go\nwaffle.SetExporter(customExporter)\n```\n\n### Logging\n\nSet a custom logger to capture Waffle's internal logs.\n\n```go\nwaffle.SetLogger(logger)\n```\n\n## Handling blocking event\n\nWhen Waffle detects an attack and blocks the request, it returns a `waf.SecurityBlockingError` error type. If you catch this error, you should handle it appropriately—for example, by returning a proper error response to the client.\nThis error type can be checked using the `waf.IsSecurityBlockingError` function.\n\nWhen Waffle's HTTP middleware blocks a request, it automatically returns an HTTP 403 Forbidden response, but it is your responsibility to handle the blocked function call.\nFor instance, if a function called during processing at an endpoint attempts to execute a potentially vulnerable SQL query (such as SQL Injection), that function call will be blocked and terminated by returning an error of type `waf.SecurityBlockingError`.\nYou can determine whether the block was initiated by the WAF using either `errors.As` or `waf.IsSecurityBlockingError`.\n\n```go\n// Example of handling a blocked SQL query\n\n// Will be blocked due to SQL Injection attempt\nuserInput := \"1 OR 1 = 1\"\n_, err := db.QueryContext(ctx, fmt.Sprintf(\"SELECT * FROM users WHERE id = '%s'\", userInput))\nif err != nil {\n    if waf.IsSecurityBlockingError(err) {\n        // Handle blocked request\n        log.Printf(\"Blocked request: %v\", err)\n        return\n    }\n\n    // Handle other errors\n    log.Fatal(err)\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsitebatch%2Fwaffle-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsitebatch%2Fwaffle-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsitebatch%2Fwaffle-go/lists"}