{"id":42671122,"url":"https://github.com/calibr/templock","last_synced_at":"2026-01-29T11:00:02.334Z","repository":{"id":79229152,"uuid":"60177812","full_name":"calibr/templock","owner":"calibr","description":" Temporary resource locking after failed attempts, usefull for mitigation password bruteforcing","archived":false,"fork":false,"pushed_at":"2017-05-30T17:35:26.000Z","size":11,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-29T19:57:37.061Z","etag":null,"topics":["authorization","brute-force","lock"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/calibr.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,"governance":null}},"created_at":"2016-06-01T13:08:18.000Z","updated_at":"2017-05-30T17:51:11.000Z","dependencies_parsed_at":null,"dependency_job_id":"7c6573dc-be4c-49e1-ab8a-f3399d76851b","html_url":"https://github.com/calibr/templock","commit_stats":{"total_commits":13,"total_committers":1,"mean_commits":13.0,"dds":0.0,"last_synced_commit":"f2216d4bb39bf685b642ba000f95e4f560a5172a"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/calibr/templock","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calibr%2Ftemplock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calibr%2Ftemplock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calibr%2Ftemplock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calibr%2Ftemplock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/calibr","download_url":"https://codeload.github.com/calibr/templock/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calibr%2Ftemplock/sbom","scorecard":{"id":262716,"data":{"date":"2025-08-11","repo":{"name":"github.com/calibr/templock","commit":"f2216d4bb39bf685b642ba000f95e4f560a5172a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.6,"checks":[{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/13 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-17T11:11:56.534Z","repository_id":79229152,"created_at":"2025-08-17T11:11:56.534Z","updated_at":"2025-08-17T11:11:56.534Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28876674,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-29T10:31:27.438Z","status":"ssl_error","status_checked_at":"2026-01-29T10:31:01.017Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6: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":["authorization","brute-force","lock"],"created_at":"2026-01-29T11:00:01.599Z","updated_at":"2026-01-29T11:00:02.324Z","avatar_url":"https://github.com/calibr.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Install\n\n`npm i templock`\n\n## Use Case\n\nThe main use case of this lib is to limit an amount of authorization attempts to some resource, simply said, to prevent credentials brute forcing. For example, if you have authorization form and want to prevent brute forcing on it, it will be a good idea to use this lib.\n\n## Basic information\n\nThe lib operates with two basic objects:\n- Item ID\n- Lock Category\n\nItem ID - it is a string representation of the item that a user wants to access. For a web site authorization form it can be just `authform`.\n\nLock Category - also a custom string, that describes the user who tries to access an item. For authorization forms, it can be user's IP: `user_ip:125.12.52.51`.\n\nEach lock category should have its strategy - set of rules that describe when and how long an item should be locked for the category. Multiple categories can have the same strategy.\n\n## Usage\n\nSuppose you have a login form and you want to limit login attempts for each user IP to 20 per 5 minutes. Let's write a strategy for this:\n\n```js\nvar ipStrategy = {\n  category: /^ip_.+?/,\n  attempts: 20,\n  lockFor: 300\n}\n```\n\n`category` - it is a regular expression or a string that used to match specific strategies\n`attempts` - amount of unsuccessful attempts before an item is locked for the strategy\n`lockFor` - amount of time in seconds to lock an item for\n\nThen we should create a new TempLock item and add the strategy to it:\n\n```js\nvar TempLock = require('templock')\n\nvar tempLock = new TempLock({\n  strategies: [ipStrategy]\n})\n```\n\nInitialization completed, then if someone logins unsuccessfully in our form we need to log their attempt and also check if the IP is locked:\n\n```\n\ntempLock.isLocked('authform', 'ip_' + remoteAddr).then(locked =\u003e {\n  if(locked) {\n    throw new AuthError('Too Many Attempts')\n  }\n  if(!login(email, password)) {\n    tempLock.addAttempt('authform', 'ip_' + remoteAddr);\n  }\n})\n```\n\nWhere `remoteAddr` is IP of the user who tries to log in.\nIf the user has attempted to log in more than 20 times, we run into the first IF clause and throw an error. That was the simplest usage of the lib.\n\n#### main category\n\nThere is the category called \"main\" which always exist and attempts to it added transparently each time you call `addAttempt`. Basically, this category is used to limit access to the entire item from any categories. For example, if some user has exceeded attempts limit by the main strategy, every user become locked for the resource by the main strategy.\n\n### Storage\n\nThe lib can store attempts information either in memory on in Redis, to setup memory database usage consider this code:\n\n```js\ntempLock.setStorage(new TempLock.MemoryStorage());\n```\n\n*Use memory storage carefully, because after node app restart all attempts data will be lost*\n\nAnd setup for the Redis storage:\n\n```js\ntempLock.setStorage(new TempLock.RedisStorage({\n  host: 'localhost',\n  port: 6379,\n}));\n```\n\nThe config object passed into the RedisStorage constructor is equivalent to [redis client options](https://github.com/NodeRedis/node_redis#options-object-properties)\n\n### Reference\n\n\n#### TempLock(config)\n\n`config` is an object containing only one property `strategies` which is an array of strategies, each strategy represents the following object:\n\n```json\n{\n  category: string | RegExp\n  attempts: number,\n  lockFor: number\n}\n```\n\n#### setStorage(storage)\n\nSets storage for further use, available storages:\n\n- MemoryStorage\n- RedisStorage\n\n#### addAttempt(itemId, categories)\n\nAdds attempt for a specific item and category or an array of categories. If categories are omitted attempt will be added to the \"main\" category.\n\n#### isLocked(itemId, categories)\n\nCheck whether an item is locked for specific categories or not. Returns a Promise. If the item is locked for at least one category, a Promise will be resolved with the `true` value.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcalibr%2Ftemplock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcalibr%2Ftemplock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcalibr%2Ftemplock/lists"}