Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
053b079d
Commit
053b079d
authored
Jan 11, 2022
by
Bogdan Denkovych
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve SSH key format validation
Changelog: other
parent
c43580ea
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
63 additions
and
22 deletions
+63
-22
app/assets/javascripts/pages/profiles/keys/index.js
app/assets/javascripts/pages/profiles/keys/index.js
+2
-0
app/assets/javascripts/profile/add_ssh_key_validation.js
app/assets/javascripts/profile/add_ssh_key_validation.js
+13
-4
app/models/key.rb
app/models/key.rb
+1
-1
app/views/profiles/keys/_form.html.haml
app/views/profiles/keys/_form.html.haml
+1
-1
spec/features/profiles/keys_spec.rb
spec/features/profiles/keys_spec.rb
+2
-2
spec/frontend/profile/add_ssh_key_validation_spec.js
spec/frontend/profile/add_ssh_key_validation_spec.js
+22
-14
spec/models/key_spec.rb
spec/models/key_spec.rb
+22
-0
No files found.
app/assets/javascripts/pages/profiles/keys/index.js
View file @
053b079d
...
@@ -7,11 +7,13 @@ function initSshKeyValidation() {
...
@@ -7,11 +7,13 @@ function initSshKeyValidation() {
const
input
=
document
.
querySelector
(
'
.js-add-ssh-key-validation-input
'
);
const
input
=
document
.
querySelector
(
'
.js-add-ssh-key-validation-input
'
);
if
(
!
input
)
return
;
if
(
!
input
)
return
;
const
supportedAlgorithms
=
JSON
.
parse
(
input
.
dataset
.
supportedAlgorithms
);
const
warning
=
document
.
querySelector
(
'
.js-add-ssh-key-validation-warning
'
);
const
warning
=
document
.
querySelector
(
'
.js-add-ssh-key-validation-warning
'
);
const
originalSubmit
=
input
.
form
.
querySelector
(
'
.js-add-ssh-key-validation-original-submit
'
);
const
originalSubmit
=
input
.
form
.
querySelector
(
'
.js-add-ssh-key-validation-original-submit
'
);
const
confirmSubmit
=
warning
.
querySelector
(
'
.js-add-ssh-key-validation-confirm-submit
'
);
const
confirmSubmit
=
warning
.
querySelector
(
'
.js-add-ssh-key-validation-confirm-submit
'
);
const
addSshKeyValidation
=
new
AddSshKeyValidation
(
const
addSshKeyValidation
=
new
AddSshKeyValidation
(
supportedAlgorithms
,
input
,
input
,
warning
,
warning
,
originalSubmit
,
originalSubmit
,
...
...
app/assets/javascripts/profile/add_ssh_key_validation.js
View file @
053b079d
export
default
class
AddSshKeyValidation
{
export
default
class
AddSshKeyValidation
{
constructor
(
inputElement
,
warningElement
,
originalSubmitElement
,
confirmSubmitElement
)
{
constructor
(
supportedAlgorithms
,
inputElement
,
warningElement
,
originalSubmitElement
,
confirmSubmitElement
,
)
{
this
.
inputElement
=
inputElement
;
this
.
inputElement
=
inputElement
;
this
.
form
=
inputElement
.
form
;
this
.
form
=
inputElement
.
form
;
this
.
supportedAlgorithms
=
supportedAlgorithms
;
this
.
publicKeyRegExp
=
new
RegExp
(
`^(
${
this
.
supportedAlgorithms
.
join
(
'
|
'
)}
)`
);
this
.
warningElement
=
warningElement
;
this
.
warningElement
=
warningElement
;
this
.
originalSubmitElement
=
originalSubmitElement
;
this
.
originalSubmitElement
=
originalSubmitElement
;
...
@@ -23,7 +32,7 @@ export default class AddSshKeyValidation {
...
@@ -23,7 +32,7 @@ export default class AddSshKeyValidation {
}
}
submit
(
event
)
{
submit
(
event
)
{
this
.
isValid
=
AddSshKeyValidation
.
isPublicKey
(
this
.
inputElement
.
value
);
this
.
isValid
=
this
.
isPublicKey
(
this
.
inputElement
.
value
);
if
(
this
.
isValid
)
return
true
;
if
(
this
.
isValid
)
return
true
;
...
@@ -37,7 +46,7 @@ export default class AddSshKeyValidation {
...
@@ -37,7 +46,7 @@ export default class AddSshKeyValidation {
this
.
originalSubmitElement
.
classList
.
toggle
(
'
hide
'
,
isVisible
);
this
.
originalSubmitElement
.
classList
.
toggle
(
'
hide
'
,
isVisible
);
}
}
static
isPublicKey
(
value
)
{
isPublicKey
(
value
)
{
return
/^
(
ssh|ecdsa-sha2
)
-/
.
test
(
value
);
return
this
.
publicKeyRegExp
.
test
(
value
);
}
}
}
}
app/models/key.rb
View file @
053b079d
...
@@ -22,7 +22,7 @@ class Key < ApplicationRecord
...
@@ -22,7 +22,7 @@ class Key < ApplicationRecord
validates
:key
,
validates
:key
,
presence:
true
,
presence:
true
,
length:
{
maximum:
5000
},
length:
{
maximum:
5000
},
format:
{
with:
/\A(
ssh|ecdsa)-.*\Z
/
}
format:
{
with:
/\A(
#{
Gitlab
::
SSHPublicKey
.
supported_algorithms
.
join
(
'|'
)
}
)
/
}
validates
:fingerprint
,
validates
:fingerprint
,
uniqueness:
true
,
uniqueness:
true
,
...
...
app/views/profiles/keys/_form.html.haml
View file @
053b079d
...
@@ -5,7 +5,7 @@
...
@@ -5,7 +5,7 @@
.form-group
.form-group
=
f
.
label
:key
,
s_
(
'Profiles|Key'
),
class:
'label-bold'
=
f
.
label
:key
,
s_
(
'Profiles|Key'
),
class:
'label-bold'
=
f
.
text_area
:key
,
class:
"form-control gl-form-input js-add-ssh-key-validation-input qa-key-public-key-field"
,
rows:
8
,
required:
true
=
f
.
text_area
:key
,
class:
"form-control gl-form-input js-add-ssh-key-validation-input qa-key-public-key-field"
,
rows:
8
,
required:
true
,
data:
{
supported_algorithms:
Gitlab
::
SSHPublicKey
.
supported_algorithms
}
%p
.form-text.text-muted
=
s_
(
'Profiles|Begins with %{ssh_key_algorithms}.'
)
%
{
ssh_key_algorithms:
ssh_key_allowed_algorithms
}
%p
.form-text.text-muted
=
s_
(
'Profiles|Begins with %{ssh_key_algorithms}.'
)
%
{
ssh_key_algorithms:
ssh_key_allowed_algorithms
}
.form-row
.form-row
.col.form-group
.col.form-group
...
...
spec/features/profiles/keys_spec.rb
View file @
053b079d
...
@@ -32,10 +32,10 @@ RSpec.describe 'Profile > SSH Keys' do
...
@@ -32,10 +32,10 @@ RSpec.describe 'Profile > SSH Keys' do
expect
(
find
(
'.breadcrumbs-sub-title'
)).
to
have_link
(
attrs
[
:title
])
expect
(
find
(
'.breadcrumbs-sub-title'
)).
to
have_link
(
attrs
[
:title
])
end
end
it
'shows a confirmable warning if the key
does not start with ssh-
'
do
it
'shows a confirmable warning if the key
begins with an algorithm name that is unsupported
'
do
attrs
=
attributes_for
(
:key
)
attrs
=
attributes_for
(
:key
)
fill_in
(
'Key'
,
with:
'
invalid-
key'
)
fill_in
(
'Key'
,
with:
'
unsupported-ssh-rsa
key'
)
fill_in
(
'Title'
,
with:
attrs
[
:title
])
fill_in
(
'Title'
,
with:
attrs
[
:title
])
click_button
(
'Add key'
)
click_button
(
'Add key'
)
...
...
spec/frontend/profile/add_ssh_key_validation_spec.js
View file @
053b079d
...
@@ -3,18 +3,18 @@ import AddSshKeyValidation from '../../../app/assets/javascripts/profile/add_ssh
...
@@ -3,18 +3,18 @@ import AddSshKeyValidation from '../../../app/assets/javascripts/profile/add_ssh
describe
(
'
AddSshKeyValidation
'
,
()
=>
{
describe
(
'
AddSshKeyValidation
'
,
()
=>
{
describe
(
'
submit
'
,
()
=>
{
describe
(
'
submit
'
,
()
=>
{
it
(
'
returns true if isValid is true
'
,
()
=>
{
it
(
'
returns true if isValid is true
'
,
()
=>
{
const
addSshKeyValidation
=
new
AddSshKeyValidation
({});
const
addSshKeyValidation
=
new
AddSshKeyValidation
(
[],
{});
jest
.
spyOn
(
A
ddSshKeyValidation
,
'
isPublicKey
'
).
mockReturnValue
(
true
);
jest
.
spyOn
(
a
ddSshKeyValidation
,
'
isPublicKey
'
).
mockReturnValue
(
true
);
expect
(
addSshKeyValidation
.
submit
()).
toBe
Truthy
(
);
expect
(
addSshKeyValidation
.
submit
()).
toBe
(
true
);
});
});
it
(
'
calls preventDefault and toggleWarning if isValid is false
'
,
()
=>
{
it
(
'
calls preventDefault and toggleWarning if isValid is false
'
,
()
=>
{
const
addSshKeyValidation
=
new
AddSshKeyValidation
({});
const
addSshKeyValidation
=
new
AddSshKeyValidation
(
[],
{});
const
event
=
{
const
event
=
{
preventDefault
:
jest
.
fn
(),
preventDefault
:
jest
.
fn
(),
};
};
jest
.
spyOn
(
A
ddSshKeyValidation
,
'
isPublicKey
'
).
mockReturnValue
(
false
);
jest
.
spyOn
(
a
ddSshKeyValidation
,
'
isPublicKey
'
).
mockReturnValue
(
false
);
jest
.
spyOn
(
addSshKeyValidation
,
'
toggleWarning
'
).
mockImplementation
(()
=>
{});
jest
.
spyOn
(
addSshKeyValidation
,
'
toggleWarning
'
).
mockImplementation
(()
=>
{});
addSshKeyValidation
.
submit
(
event
);
addSshKeyValidation
.
submit
(
event
);
...
@@ -31,14 +31,15 @@ describe('AddSshKeyValidation', () => {
...
@@ -31,14 +31,15 @@ describe('AddSshKeyValidation', () => {
warningElement
.
classList
.
add
(
'
hide
'
);
warningElement
.
classList
.
add
(
'
hide
'
);
const
addSshKeyValidation
=
new
AddSshKeyValidation
(
const
addSshKeyValidation
=
new
AddSshKeyValidation
(
[],
{},
{},
warningElement
,
warningElement
,
originalSubmitElement
,
originalSubmitElement
,
);
);
addSshKeyValidation
.
toggleWarning
(
true
);
addSshKeyValidation
.
toggleWarning
(
true
);
expect
(
warningElement
.
classList
.
contains
(
'
hide
'
)).
toBe
Falsy
(
);
expect
(
warningElement
.
classList
.
contains
(
'
hide
'
)).
toBe
(
false
);
expect
(
originalSubmitElement
.
classList
.
contains
(
'
hide
'
)).
toBe
Truthy
(
);
expect
(
originalSubmitElement
.
classList
.
contains
(
'
hide
'
)).
toBe
(
true
);
});
});
it
(
'
hides warningElement and shows originalSubmitElement if isVisible is false
'
,
()
=>
{
it
(
'
hides warningElement and shows originalSubmitElement if isVisible is false
'
,
()
=>
{
...
@@ -47,25 +48,32 @@ describe('AddSshKeyValidation', () => {
...
@@ -47,25 +48,32 @@ describe('AddSshKeyValidation', () => {
originalSubmitElement
.
classList
.
add
(
'
hide
'
);
originalSubmitElement
.
classList
.
add
(
'
hide
'
);
const
addSshKeyValidation
=
new
AddSshKeyValidation
(
const
addSshKeyValidation
=
new
AddSshKeyValidation
(
[],
{},
{},
warningElement
,
warningElement
,
originalSubmitElement
,
originalSubmitElement
,
);
);
addSshKeyValidation
.
toggleWarning
(
false
);
addSshKeyValidation
.
toggleWarning
(
false
);
expect
(
warningElement
.
classList
.
contains
(
'
hide
'
)).
toBe
Truthy
(
);
expect
(
warningElement
.
classList
.
contains
(
'
hide
'
)).
toBe
(
true
);
expect
(
originalSubmitElement
.
classList
.
contains
(
'
hide
'
)).
toBe
Falsy
(
);
expect
(
originalSubmitElement
.
classList
.
contains
(
'
hide
'
)).
toBe
(
false
);
});
});
});
});
describe
(
'
isPublicKey
'
,
()
=>
{
describe
(
'
isPublicKey
'
,
()
=>
{
it
(
'
returns false if probably invalid public ssh key
'
,
()
=>
{
it
(
'
returns false if value begins with an algorithm name that is unsupported
'
,
()
=>
{
expect
(
AddSshKeyValidation
.
isPublicKey
(
'
nope
'
)).
toBeFalsy
();
const
addSshKeyValidation
=
new
AddSshKeyValidation
([
'
ssh-rsa
'
,
'
ssh-algorithm
'
],
{});
expect
(
addSshKeyValidation
.
isPublicKey
(
'
nope key
'
)).
toBe
(
false
);
expect
(
addSshKeyValidation
.
isPublicKey
(
'
ssh- key
'
)).
toBe
(
false
);
expect
(
addSshKeyValidation
.
isPublicKey
(
'
unsupported-ssh-rsa key
'
)).
toBe
(
false
);
});
});
it
(
'
returns true if probably valid public ssh key
'
,
()
=>
{
it
(
'
returns true if value begins with an algorithm name that is supported
'
,
()
=>
{
expect
(
AddSshKeyValidation
.
isPublicKey
(
'
ssh-
'
)).
toBeTruthy
();
const
addSshKeyValidation
=
new
AddSshKeyValidation
([
'
ssh-rsa
'
,
'
ssh-algorithm
'
],
{});
expect
(
AddSshKeyValidation
.
isPublicKey
(
'
ecdsa-sha2-
'
)).
toBeTruthy
();
expect
(
addSshKeyValidation
.
isPublicKey
(
'
ssh-rsa key
'
)).
toBe
(
true
);
expect
(
addSshKeyValidation
.
isPublicKey
(
'
ssh-algorithm key
'
)).
toBe
(
true
);
});
});
});
});
});
});
spec/models/key_spec.rb
View file @
053b079d
...
@@ -21,6 +21,28 @@ RSpec.describe Key, :mailer do
...
@@ -21,6 +21,28 @@ RSpec.describe Key, :mailer do
it
{
is_expected
.
to
allow_value
(
attributes_for
(
:ecdsa_key_256
)[
:key
]).
for
(
:key
)
}
it
{
is_expected
.
to
allow_value
(
attributes_for
(
:ecdsa_key_256
)[
:key
]).
for
(
:key
)
}
it
{
is_expected
.
to
allow_value
(
attributes_for
(
:ed25519_key_256
)[
:key
]).
for
(
:key
)
}
it
{
is_expected
.
to
allow_value
(
attributes_for
(
:ed25519_key_256
)[
:key
]).
for
(
:key
)
}
it
{
is_expected
.
not_to
allow_value
(
'foo-bar'
).
for
(
:key
)
}
it
{
is_expected
.
not_to
allow_value
(
'foo-bar'
).
for
(
:key
)
}
context
'key format'
do
let
(
:key
)
{
build
(
:key
)
}
it
'does not allow the key that begins with an algorithm name that is unsupported'
do
key
.
key
=
'unsupported-ssh-rsa key'
key
.
valid?
expect
(
key
.
errors
.
of_kind?
(
:key
,
:invalid
)).
to
eq
(
true
)
end
Gitlab
::
SSHPublicKey
.
supported_algorithms
.
each
do
|
supported_algorithm
|
it
"allows the key that begins with supported algorithm name '
#{
supported_algorithm
}
'"
do
key
.
key
=
"
#{
supported_algorithm
}
key"
key
.
valid?
expect
(
key
.
errors
.
of_kind?
(
:key
,
:invalid
)).
to
eq
(
false
)
end
end
end
end
end
describe
"Methods"
do
describe
"Methods"
do
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment