{"id":26066434,"url":"https://github.com/psecio/canary","last_synced_at":"2025-06-23T19:04:04.313Z","repository":{"id":57045460,"uuid":"121312693","full_name":"psecio/canary","owner":"psecio","description":"Canary: Input Detection and Response","archived":false,"fork":false,"pushed_at":"2019-07-28T21:22:34.000Z","size":36,"stargazers_count":30,"open_issues_count":1,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-25T12:40:31.037Z","etag":null,"topics":["canary","detection","security","security-hardening","security-tools"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/psecio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-02-12T22:51:52.000Z","updated_at":"2021-11-29T10:31:03.000Z","dependencies_parsed_at":"2022-08-24T03:40:20.735Z","dependency_job_id":null,"html_url":"https://github.com/psecio/canary","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psecio%2Fcanary","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psecio%2Fcanary/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psecio%2Fcanary/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/psecio%2Fcanary/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/psecio","download_url":"https://codeload.github.com/psecio/canary/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248439376,"owners_count":21103592,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["canary","detection","security","security-hardening","security-tools"],"created_at":"2025-03-08T20:51:59.922Z","updated_at":"2025-04-11T16:25:53.123Z","avatar_url":"https://github.com/psecio.png","language":"PHP","readme":"Canary: Input Detection and Response\n==================\n\n[![Build Status](https://travis-ci.org/psecio/canary.svg?branch=master)](https://travis-ci.org/psecio/canary)\n\nThe origin of the term \"canary\" (as a method of detection) was originally used by those that worked deep in mines and would take a canary\n(the bird) with them to detect gas or other reasons they needed to leave. If the bird started behaving oddly they knew something was amiss.\nThis same concept is applied in the security world and is similarly called a \"canary\".\n\nSimilarly, the `Canary` library allows you to define key/value combinations that can be used to detect when certain data is used and notify\nyou using a variety of methods including the default PHP error log, log handling via `Monolog` and messages to `Slack` channels.\n\nFor example, you may generate a special username that you want to use as a trigger. This username isn't actually a user in your system\nbut you do want to be notified if a login attempt is made using it. `Canary` makes this simple by defining checks with an `if` method and,\noptionally, a handler using a `then` method. For example, say we generated the username of `canary1234@foo.com` and we want to detect when\nit's used. You can define this in a `Canary` expression like so:\n\n```php\n\u003c?php\n$_POST = [\n    'username' =\u003e 'canary1234@foo.com',\n    'password' =\u003e 'sup3rs3cr3t'\n];\n\n\\Psecio\\Canary\\Instance::build()-\u003eif('username', 'canary1234@foo.com')-\u003eexecute();\n\n// Or you can set multiple match values to look for with an array\n$matches = [\n    'username' =\u003e 'canary1234@foo.com',\n    'password' =\u003e 'sup3rs3cr3t'\n];\n\\Psecio\\Canary\\Instance::build()-\u003eif($matches)-\u003eexecute();\n?\u003e\n```\n\nIn this example we're looking at the current input and checking to see if there's a `username` value of `canary1234@foo.com`. In the case\nof our current `$_POST` values, there's a match. By default (if no `then` handler is defined) the information about the match is output to\nthe error like (via the `Psecio\\Canary\\Notify\\ErrorLog` handler). The JSON encoded result looks like this:\n\n```\n{\"type\":\"equals\",\"key\":\"username\",\"value\":\"canary1234@foo.com\"}\n```\n\n\u003e **NOTE:** Canary automatically pulls in the `$_GET` and `$_POST` superglobal values for evaluation so you don't need to manually pass\nthen in.\n\n### Using an external data source\n\n`Canary` also allows you to use a (static) class method to provide the `if` portion of the evaluation with data. To use it, just pass in the class\nand static method name as a string:\n\n```\n\u003c?php\n$classMethod = '\\Foo\\Bar::criteria';\n\n\\Psecio\\Canary\\Instance::build()-\u003eif($classMethod)-\u003eexecute();\n?\u003e\n```\n\nThe return from this method must be an array otherwise an exception will be thrown.\n\n### Supported Notifier Methods\n\nCurrently `Canary` supports the following notification methods:\n\n| Type      | Class                            | Expected Input              |\n| --------- | -------------------------------- | --------------------------- |\n| Error log | `\\Psecio\\Canary\\Notify\\ErrorLog` | None, uses default location |\n| Monolog   | `\\Psecio\\Canary\\Notify\\Monolog`  | `\\Monolog\\Logger`           |\n| Callback  | `\\Psecio\\Canary\\Notify\\Callback` | Callable function           |\n| Slack     | `\\Psecio\\Canary\\Notify\\Slack`    | `\\Maknz\\Slack\\Client`       |\n| PagerDuty | `\\Psecio\\Canary\\Notify\\PagerDuty`| `\\PagerDuty\\Event`          |\n\n### Creating a Custom Handler (Callback)\n\nIf you don't want your results to go to the error log, you can create your own handler via the `then` method. Currently the only custom\nhandler supported is a callable method. So, say we wanted to output a message to the user of our special username and kill the script. We\nmight use something like this:\n\n```php\n\u003c?php\n$_POST = ['username' =\u003e 'canary1234@foo.com'];\n\n\\Psecio\\Canary\\Instance::build()-\u003eif('username', 'canary1234@foo.com')\n    -\u003ethen(function($criteria) {\n        die(\"You shouldn't have done that!\");\n    })\n    -\u003eexecute();\n?\u003e\n```\n\nIn this handler, when it detects that the username value matches our criteria, the callback is executed and the `die` call kills the script.\n\n### Passing in custom data\n\nYou can also provide your own data set if you don't want to auto-load the current `$_GET` and `$_POST` values. To pass the data in you can use the\n`data` value in the configuration and passing it in:\n\n```php\n\u003c?php\n$config = ['data' =\u003e [\n    'username' =\u003e 'foobar@baz.com'\n]];\n\\Psecio\\Canary\\Instance::build($config)-\u003eif('username', 'canary1234@foo.com')-\u003eexecute();\n?\u003e\n```\n\n### Using a default logger\n\nYou can set it as the default logger for **all** `if` checks via the `notify` key in the `build()` configuration options:\n\n```php\n\u003c?php\n\n// create a log channel\n$log = new Logger('name');\n$log-\u003epushHandler(new StreamHandler('/tmp/mylog.log', Logger::WARNING));\n\n$config = [\n    'notify' =\u003e $log\n];\n\\Psecio\\Canary\\Instance::build($config)-\u003eif('username', 'canary1234@foo.com')-\u003eexecute();\n\n?\u003e\n```\n\n\u003e **NOTE:** If you provide a default handler via the `notify` configuration it will override all other custom notification methods.\n\n\n### Using Monolog\n\nThe `Canary` tool also allows you to use the [Monolog](https://github.com/Seldaek/monolog) logging library to define a bit more customization to the structure of the data and how it's output. Like before, we create the `Canary` instance but for the input of the `then` method we provide a `Monolog\\Logger` instance:\n\n```php\n\u003c?php\nuse Monolog\\Logger;\nuse Monolog\\Handler\\StreamHandler;\n\nrequire_once 'vendor/autoload.php';\n\n$_GET = ['username' =\u003e 'test'];\n\n// create a log channel\n$log = new Logger('name');\n$log-\u003epushHandler(new StreamHandler('/tmp/mylog.log', Logger::WARNING));\n\n\\Psecio\\Canary\\Instance::build()\n    -\u003eif('username', 'canary1234@foo.com')\n    -\u003ethen($log)\n    -\u003eexecute();\n?\u003e\n```\n\n### Using Slack\n\nYou can also make use of the [Maknz\\Slack](https://github.com/maknz/slack) library to send messages to Slack when a canary is triggered:\n\n```php\n\u003c?php\n$settings = [\n\t'channel' =\u003e '#my-channel-name',\n\t'link_names' =\u003e true\n];\n$slack = new Maknz\\Slack\\Client('https://hooks.slack.com/services/.....', $settings);\n\n\\Psecio\\Canary\\Instance::build($config)-\u003eif('username', 'canary1234@foo.com')-\u003ethen($slack);\n?\u003e\n```\n\nYou'll need to [set up an incoming webhook](https://my.slack.com/services/new/incoming-webhook) and replace the URL value in the `Client`\ncreate with the custom URL you're given. The default name for the notifications is `Canary Agent` and the output includes the same JSON\ninformation as the other notification methods.\n\n### Using PagerDuty\n\n`Canary` also allows you to send notifications to your account on the PagerDuty service using the [nmcquay/pagerduty](https://github.com/nmcquay/pagerduty) library:\n\n```php\n\u003c?php\n$pager = new \\PagerDuty\\Event();\n$pager-\u003esetServiceKey('[.... your service ID ....]');\n\n\\Psecio\\Canary\\Instance::build($config)-\u003eif('username', 'canary1234@foo.com')-\u003ethen($pager);\n?\u003e\n```\n\nYou can find the service ID by going to your services page (`https://[your domain].pagerduty.com/services`) and clicking on the service you want to use. The ID is under the \"Integrations\" tab as the \"Integration Key\".\n","funding_links":[],"categories":["Secure Programming"],"sub_categories":["Tokens"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpsecio%2Fcanary","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpsecio%2Fcanary","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpsecio%2Fcanary/lists"}