Skip to content

Commit 112d406

Browse files
authored
Merge pull request #230 from replydev/fix/broken_edit_command
Fix/broken_edit_command
2 parents 172c38c + 9d6e4d3 commit 112d406

File tree

8 files changed

+86
-44
lines changed

8 files changed

+86
-44
lines changed

.github/workflows/build.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,11 @@ jobs:
3838

3939
- name: Run cargo test
4040
run: cargo test
41+
42+
- name: Run cargo build
43+
run: cargo build
44+
45+
- uses: actions/[email protected]
46+
with:
47+
name: bins-${{ matrix.os }}
48+
path: target/debug/cotp*

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cotp"
3-
version = "1.2.4"
3+
version = "1.2.5"
44
authors = ["replydev <[email protected]>"]
55
edition = "2021"
66
description = "Trustworthy, encrypted, command-line TOTP/HOTP authenticator app with import functionality."

src/args.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ pub fn get_matches() -> ArgMatches {
128128
.long("index")
129129
.help("OTP Code index")
130130
.num_args(1)
131+
.value_parser(value_parser!(usize))
131132
.required(true),
132133
)
133134
.arg(
@@ -136,23 +137,23 @@ pub fn get_matches() -> ArgMatches {
136137
.long("issuer")
137138
.help("OTP Code issuer")
138139
.num_args(1)
139-
.required_unless_present_any(["label", "algorithm", "digits", "counter"]),
140+
.required_unless_present_any(["label", "algorithm", "digits", "counter", "pin", "change-secret"]),
140141
)
141142
.arg(
142143
Arg::new("label")
143144
.short('l')
144145
.long("label")
145146
.help("OTP Code label")
146147
.num_args(1)
147-
.required_unless_present_any(["issuer", "algorithm", "digits", "counter"]),
148+
.required_unless_present_any(["issuer", "algorithm", "digits", "counter", "pin", "change-secret"]),
148149
)
149150
.arg(
150151
Arg::new("algorithm")
151152
.short('a')
152153
.long("algorithm")
153154
.help("OTP Code algorithm")
154155
.num_args(1)
155-
.required_unless_present_any(["label", "issuer", "digits", "counter"])
156+
.required_unless_present_any(["label", "issuer", "digits", "counter", "pin", "change-secret"])
156157
.value_parser(["SHA1", "SHA256", "SHA512"]),
157158
)
158159
.arg(
@@ -162,7 +163,7 @@ pub fn get_matches() -> ArgMatches {
162163
.help("OTP Code digits")
163164
.num_args(1)
164165
.value_parser(value_parser!(u64))
165-
.required_unless_present_any(["label", "algorithm", "issuer", "counter"]),
166+
.required_unless_present_any(["label", "algorithm", "issuer", "counter", "pin", "change-secret"]),
166167
)
167168
.arg(
168169
Arg::new("period")
@@ -171,7 +172,7 @@ pub fn get_matches() -> ArgMatches {
171172
.help("OTP Code period")
172173
.num_args(1)
173174
.value_parser(value_parser!(u64))
174-
.required_unless_present_any(["label", "algorithm", "issuer", "counter"]),
175+
.required_unless_present_any(["label", "algorithm", "issuer", "counter", "pin", "change-secret"]),
175176
)
176177
.arg(
177178
Arg::new("counter")
@@ -180,14 +181,23 @@ pub fn get_matches() -> ArgMatches {
180181
.help("HOTP code counter (only for HOTP codes)")
181182
.num_args(1)
182183
.value_parser(value_parser!(u64))
183-
.required_unless_present_any(["label", "algorithm", "issuer", "digits"]),
184+
.required_unless_present_any(["label", "algorithm", "issuer", "digits", "pin", "change-secret"]),
185+
)
186+
.arg(
187+
Arg::new("pin")
188+
.short('p')
189+
.long("pin")
190+
.help("Code pin (for Yandex and MOTP)")
191+
.num_args(1)
192+
.required_unless_present_any(["label", "algorithm", "issuer", "digits", "counter", "change-secret"]),
184193
)
185194
.arg(
186195
Arg::new("change-secret")
187196
.short('k')
188197
.long("change-secret")
189198
.help("Change the OTP code secret")
190-
.action(ArgAction::SetTrue),
199+
.action(ArgAction::SetTrue)
200+
.required_unless_present_any(["label", "algorithm", "issuer", "digits", "counter", "pin"]),
191201
),
192202
)
193203
.subcommand(

src/argument_functions.rs

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -99,44 +99,59 @@ pub fn edit(matches: &ArgMatches, database: &mut OTPDatabase) -> Result<String,
9999
};
100100

101101
let index = *matches.get_one::<usize>("index").unwrap();
102-
let otp_element: Option<&OTPElement> = database.get_element(index);
103102

104-
let issuer = matches.get_one::<String>("issuer").cloned();
105-
let label = matches.get_one::<String>("label").cloned();
106-
let digits = matches.get_one::<u64>("usize").copied();
107-
let period = matches.get_one::<u64>("period").copied();
108-
let counter = matches.get_one::<u64>("counter").copied();
109-
let pin = matches.get_one::<String>("label").cloned();
110-
111-
match otp_element {
112-
Some(v) => {
113-
let mut element = v.clone();
103+
if let Some(real_index) = index
104+
// User provides row number from dashboard which is equal to the array index plus one
105+
.checked_sub(1)
106+
{
107+
if real_index >= database.elements_ref().len() {
108+
return Err(format!("{index} is an invalid index"));
109+
}
114110

115-
if let Some(v) = issuer {
116-
element.issuer = v;
117-
}
118-
if let Some(v) = label {
119-
element.label = v;
120-
}
121-
if let Some(v) = digits {
122-
element.digits = v;
123-
}
124-
if let Some(v) = period {
125-
element.period = v;
111+
let otp_element: Option<&OTPElement> = database.get_element(real_index);
112+
113+
let issuer = matches.get_one::<String>("issuer").cloned();
114+
let label = matches.get_one::<String>("label").cloned();
115+
let digits = matches.get_one::<u64>("digits").copied();
116+
let period = matches.get_one::<u64>("period").copied();
117+
let counter = matches.get_one::<u64>("counter").copied();
118+
let pin = matches.get_one::<String>("pin").cloned();
119+
120+
match otp_element {
121+
Some(v) => {
122+
let mut element = v.clone();
123+
124+
if let Some(v) = issuer {
125+
element.issuer = v;
126+
}
127+
if let Some(v) = label {
128+
element.label = v;
129+
}
130+
if let Some(v) = digits {
131+
element.digits = v;
132+
}
133+
if let Some(v) = period {
134+
element.period = v;
135+
}
136+
if counter.is_some() {
137+
element.counter = counter;
138+
}
139+
if pin.is_some() {
140+
element.pin = pin;
141+
}
142+
if secret.is_some() {
143+
element.secret = secret.clone().unwrap();
144+
}
145+
database.edit_element(real_index, element);
126146
}
127-
if counter.is_some() {
128-
element.counter = counter;
129-
}
130-
if pin.is_some() {
131-
element.pin = pin;
132-
}
133-
database.edit_element(index, element);
147+
None => return Err(format!("No element found at index {index}")),
134148
}
135-
None => return Err(format!("No element found at index {index}")),
136-
}
137149

138-
secret.zeroize();
139-
Ok(String::from("Success."))
150+
secret.zeroize();
151+
Ok(String::from("Success."))
152+
} else {
153+
Err(format! {"{index} is an invalid index"})
154+
}
140155
}
141156

142157
pub fn export(matches: &ArgMatches, database: &mut OTPDatabase) -> Result<String, String> {

src/otp/algorithms/yandex_otp_maker.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ where
7070
Err(e) => return Err(OtpError::SecretEncoding(e.kind, e.position)),
7171
};
7272

73+
if decoded_secret.len() < SECRET_LENGHT {
74+
return Err(OtpError::ShortSecret);
75+
}
76+
7377
let parsed_secret = &decoded_secret.as_slice()[0..SECRET_LENGHT];
7478

7579
let mut pin_with_secret: Vec<u8> = Vec::with_capacity(pin.as_bytes().len() + SECRET_LENGHT);

src/otp/otp_element.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,11 @@ impl OTPDatabase {
138138
}
139139

140140
pub fn sort(&mut self) {
141-
self.elements
142-
.sort_unstable_by(|c1, c2| c1.issuer.cmp(&c2.issuer))
141+
self.elements.sort_unstable_by(|c1, c2| {
142+
c1.issuer
143+
.to_ascii_lowercase()
144+
.cmp(&c2.issuer.to_ascii_lowercase())
145+
})
143146
}
144147
}
145148

src/otp/otp_error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::fmt::{Display, Formatter};
55
pub enum OtpError {
66
SecretEncoding(DecodeKind, usize), // Secret encoding error, of given kind at give position
77
MissingPin, // Missing Pin for Yandex / MOTP Codes
8+
ShortSecret, // Short secret for Yandex codes
89
MissingCounter, // Missing counter for HOTP codes
910
InvalidOffset, // Invalid offset
1011
InvalidDigest, // Invalid digest
@@ -20,6 +21,7 @@ impl Display for OtpError {
2021
OtpError::MissingCounter => f.write_str("Missing counter value"),
2122
OtpError::InvalidDigest => f.write_str("Invalid digest"),
2223
OtpError::InvalidOffset => f.write_str("Invalid offset"),
24+
OtpError::ShortSecret => f.write_str("Secret length less than 16 bytes"),
2325
}
2426
}
2527
}

0 commit comments

Comments
 (0)