-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathrecaptcha2.coffee
141 lines (121 loc) · 5.73 KB
/
recaptcha2.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
nock = require "nock"
should = require "should"
Recaptcha2 = require "../index.coffee"
recaptcha2 = new Recaptcha2 siteKey: "public_site_key", secretKey: "secret_key"
GOOGLE_CAPTCHA_ENDPOINT = "https://www.google.com/recaptcha/api/siteverify"
RECAPTCHA_RESPONSE_OK =
"success": true
"challenge_ts": Date.now()
"hostname": "127.0.0.1"
"error-codes": [
"invalid-input-response"
]
RECAPTCHA_RESPONSE_ERROR =
"success": false
"challenge_ts": Date.now()
"hostname": "127.0.0.1"
"error-codes": [
"invalid-input-response",
"invalid-input-secret"
]
describe "recaptcha2", ->
it "has a default config", ->
recaptcha2.config.should.eql siteKey: "public_site_key", secretKey: "secret_key", ssl: true
it "has a default secure endpoint", ->
recaptcha2.apiEndpoint.should.eql GOOGLE_CAPTCHA_ENDPOINT
it "has an unsecure endpoint when ssl disabled", ->
unsecureRecaptcha2 = new Recaptcha2 siteKey: "public_site_key", secretKey: "secret_key", ssl: false
unsecureRecaptcha2.apiEndpoint.should.eql GOOGLE_CAPTCHA_ENDPOINT.replace("https", "http")
describe "validate", ->
describe "when there is a valid frontend captcha response", ->
it "resolves as successful", ()->
postData = response: "valid_captcha_response", remoteip: "127.0.0.1", secret: "secret_key"
scope = nock("https://www.google.com")
.post("/recaptcha/api/siteverify", postData).reply 200, RECAPTCHA_RESPONSE_OK
recaptcha2.validate("valid_captcha_response", "127.0.0.1")
.then (response)->
response.should.eql true
.catch (error)->
should.not.exist error
describe "when there is an empty frontend captcha response", ->
it "rejects", ()->
recaptcha2.validate("")
.catch (errors)->
should.exist errors
errors.should.eql ['missing-input-response']
describe "when there is an invalid frontend captcha response", ->
it "rejects", ()->
postData = response: "invalid_captcha_response", remoteip: "127.0.0.1", secret: "secret_key"
scope = nock("https://www.google.com")
.post("/recaptcha/api/siteverify", postData).reply 200, RECAPTCHA_RESPONSE_ERROR
recaptcha2.validate("invalid_captcha_response", "127.0.0.1")
.catch (errors)->
should.exist errors
errors.should.eql ['invalid-input-response', "invalid-input-secret"]
describe "when there is a request error", ->
it "rejects", ()->
postData = response: "valid_captcha_response", remoteip: "127.0.0.1", secret: "secret_key"
scope = nock("https://www.google.com")
.post("/recaptcha/api/siteverify", postData).replyWithError 500
recaptcha2.validate("valid_captcha_response", "127.0.0.1")
.catch (errors)->
should.exist errors
errors.should.eql ['request-error', "Error: 500"]
describe "validateRequest", ->
describe "when there is a valid frontend captcha response", ->
it "resolves as successful", ()->
postData = response: "valid_captcha_response", remoteip: "127.0.0.1", secret: "secret_key"
scope = nock("https://www.google.com")
.post("/recaptcha/api/siteverify", postData).reply 200, RECAPTCHA_RESPONSE_OK
recaptcha2.validateRequest({body: {'g-recaptcha-response': "valid_captcha_response"}}, "127.0.0.1")
.then (response)->
response.should.eql true
.catch (error)->
should.not.exist error
describe "getRequestOptions", ->
body = response: "g-recaptcha_frontend_response", remoteip: "origin_ip"
it "returns the request options with the given form body", ->
recaptcha2.getRequestOptions(body).should.eql
uri: GOOGLE_CAPTCHA_ENDPOINT
method: "POST"
json: true
form:
response: "g-recaptcha_frontend_response"
remoteip: "origin_ip"
secret: "secret_key"
describe "translateErrors", ->
describe "when the given error is a string", ->
it "returns a verbose string error", ->
recaptcha2.translateErrors("request-error").should.eql "Api request failed."
describe "when the given error is an array", ->
it "returns a verbose errors array", ->
errors = [
'missing-input-secret', 'invalid-input-secret',
'missing-input-response', 'invalid-input-response'
]
readableErrors = [
'The secret parameter is missing.'
'The secret parameter is invalid or malformed.'
'The response parameter is missing.'
'The response parameter is invalid or malformed.'
]
recaptcha2.translateErrors(errors).should.eql readableErrors
describe "formElement", ->
describe "when there is a given html class", ->
it "returns a div with the given class", ->
div = '<div class="test-class class2" data-sitekey="public_site_key"></div>'
recaptcha2.formElement("test-class class2").should.eql div
describe "when there is no given html class", ->
it "returns a div with the default class", ->
div = '<div class="g-recaptcha" data-sitekey="public_site_key"></div>'
recaptcha2.formElement().should.eql div
describe "security tests", ->
attackVectorClass='" OnErRor=alert(123) b="'
attackVectorSecretKey='" OnErRor=alert(321) b="'
insecureRecaptcha = new Recaptcha2 siteKey: attackVectorSecretKey, secretKey: "secret_key"
describe "html class name is present", ->
it "returns a div with the escaped class", ->
insecureRecaptcha.formElement(attackVectorClass).should.not.containEql attackVectorClass
describe "when sitekey is malicious", ->
it "returns a div with the escaped sitekey", ->
insecureRecaptcha.formElement().should.not.containEql attackVectorSecretKey