Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

encoder: Add morse code encode decode #5967

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions addOns/encoder/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Added
- A predefined processor "ASCify" which converts text removing accents/diacritics/ligatures (perhaps not fully, due to operation in compatibility mode) leaving only ASCII characters.
- Predefined processors for encoding and decoding Morse Code.

## [1.5.0] - 2024-05-07
### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
import org.zaproxy.addon.encoder.processors.predefined.JavaScriptStringDecoder;
import org.zaproxy.addon.encoder.processors.predefined.JavaScriptStringEncoder;
import org.zaproxy.addon.encoder.processors.predefined.Md5Hasher;
import org.zaproxy.addon.encoder.processors.predefined.MorseDecoder;
import org.zaproxy.addon.encoder.processors.predefined.MorseEncoder;
import org.zaproxy.addon.encoder.processors.predefined.PowerShellEncoder;
import org.zaproxy.addon.encoder.processors.predefined.Sha1Hasher;
import org.zaproxy.addon.encoder.processors.predefined.Sha256Hasher;
Expand Down Expand Up @@ -103,8 +105,11 @@ public class EncodeDecodeProcessors {
addPredefined("reverse", Reverse.getSingleton());
addPredefined("lowercase", LowerCase.getSingleton());
addPredefined("uppercase", UpperCase.getSingleton());

addPredefined("powershellencode", PowerShellEncoder.getSingleton());
addPredefined("ascify", Ascify.getSingleton());
addPredefined("morsecodeencode", MorseEncoder.getSingleton());
addPredefined("morsecodeeecode", MorseDecoder.getSingleton());
}

private Map<String, EncodeDecodeProcessorItem> scriptProcessors = new HashMap<>();
Expand Down Expand Up @@ -149,7 +154,7 @@ private List<EncodeDecodeProcessorItem> getScriptProcessors() {
return scriptProcessors.values().stream().collect(Collectors.toList());
}

private EncodeDecodeProcessorItem createItemFromScriptWrapper(ScriptWrapper ws) {
private static EncodeDecodeProcessorItem createItemFromScriptWrapper(ScriptWrapper ws) {
String scriptName = ws.getName();
ScriptBasedEncodeDecodeProcessor processor =
new ScriptBasedEncodeDecodeProcessor(ws.getName());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2024 The ZAP Development Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zaproxy.addon.encoder.processors.predefined;

import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.parosproxy.paros.Constant;
import org.zaproxy.addon.encoder.processors.EncodeDecodeProcessor;
import org.zaproxy.addon.encoder.processors.EncodeDecodeResult;

public class MorseDecoder implements EncodeDecodeProcessor {

private static final Pattern VALID_CHARS = Pattern.compile("[// .-]*");
private static final Map<String, Character> CHARACTER_MAP =
MorseEncoder.CHARACTER_MAP.entrySet().stream()
.collect(Collectors.toUnmodifiableMap(Map.Entry::getValue, Map.Entry::getKey));

private static final MorseDecoder INSTANCE = new MorseDecoder();

@Override
public EncodeDecodeResult process(String value) {
if (value.isBlank()) {
return new EncodeDecodeResult("");
}
// Replace em dash with standard hyphen
value = value.replace("—", "-");
if (!VALID_CHARS.matcher(value).matches()) {
return EncodeDecodeResult.withError(Constant.messages.getString("encoder.morse.error"));
}

StringBuilder out = new StringBuilder(value.length() * 3);
for (String words : value.split("/")) {
for (String character : words.split(" ")) {
out.append(CHARACTER_MAP.get(character));
}
out.append(' ');
}
return new EncodeDecodeResult(out.toString().trim());
}

public static MorseDecoder getSingleton() {
return INSTANCE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2024 The ZAP Development Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zaproxy.addon.encoder.processors.predefined;

import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
import org.parosproxy.paros.Constant;
import org.zaproxy.addon.encoder.processors.EncodeDecodeProcessor;
import org.zaproxy.addon.encoder.processors.EncodeDecodeResult;

public class MorseEncoder implements EncodeDecodeProcessor {

private static final Pattern VALID_CHARS = Pattern.compile("[A-Z0-9 ]*");
protected static final Map<Character, String> CHARACTER_MAP =
Map.ofEntries(
Map.entry('A', ".-"),
Map.entry('B', "-..."),
Map.entry('C', "-.-."),
Map.entry('D', "-.."),
Map.entry('E', "."),
Map.entry('F', "..-."),
Map.entry('G', "--."),
Map.entry('H', "...."),
Map.entry('I', ".."),
Map.entry('J', ".---"),
Map.entry('K', "-.-"),
Map.entry('L', ".-.."),
Map.entry('M', "--"),
Map.entry('N', "-."),
Map.entry('O', "---"),
Map.entry('P', ".--."),
Map.entry('Q', "--.-"),
Map.entry('R', ".-."),
Map.entry('S', "..."),
Map.entry('T', "-"),
Map.entry('U', "..-"),
Map.entry('V', "...-"),
Map.entry('W', ".--"),
Map.entry('X', "-..-"),
Map.entry('Y', "-.--"),
Map.entry('Z', "--.."),

// Replace space as slash which is word separator
// https://morsecode.world/international/translator.html
Map.entry(' ', "/"),
Map.entry('1', ".----"),
Map.entry('2', "..---"),
Map.entry('3', "...--"),
Map.entry('4', "....-"),
Map.entry('5', "....."),
Map.entry('6', "-...."),
Map.entry('7', "--..."),
Map.entry('8', "---.."),
Map.entry('9', "----."),
Map.entry('0', "-----"));

private static final MorseEncoder INSTANCE = new MorseEncoder();

@Override
public EncodeDecodeResult process(String value) {
value = value.toUpperCase(Locale.ROOT);
if (!VALID_CHARS.matcher(value).matches()) {
return EncodeDecodeResult.withError(Constant.messages.getString("encoder.morse.error"));
}

StringBuilder out = new StringBuilder(value.length() * 3);
String outSeq;
for (Character c : value.toCharArray()) {
outSeq = CHARACTER_MAP.get(c);
if (outSeq.equals("/")) {
// This is a word break, remove the previous trailing space
out.deleteCharAt(out.length() - 1);
out.append(outSeq);
} else {
out.append(outSeq).append(' ');
}
}
return new EncodeDecodeResult(out.toString().trim());
}

public static MorseEncoder getSingleton() {
return INSTANCE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ <H4>Unescaped Unicode Text</H4>
Will display the unescaped Unicode characters. For example, the text
<code>%u0041%u00e7%u006f%u0072%u0065%u0073</code> would be decoded as <code>A&ccedil;ores</code>.

<H4>Morse Code Encoder</H4>
Will display dits (.) and dahs (-) and word breaks (/) representing the provided AlphaNumeric (including space) input.
For example, the text <code>SOS SOS</code> would be encoded as <code>... --- .../... --- ...</code>.

<H3>Decoders</H3>

<H4>ASCII Hex Decode</H4>
Expand Down Expand Up @@ -174,6 +178,10 @@ <H4>URL Decode</H4>
<H4>Full URL Decode</H4>
Will display the URL decoding of the text you enter (percent signs removed and HEX decoded).

<H4>Morse Code Decoder</H4>
Will display AlphaNumeric (including space) output representing the provided morrse code input.
For example, the text <code>... --- .../... --- ...</code> would be encoded as <code>SOS SOS</code>.

<H3>Hashers</H3>

<H4>MD5 Hash</H4>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ encoder.dialog.reset.button.tooltip = Reset all tabs and output panels to defaul
encoder.dialog.reset.confirm = All Encode/Decode/Hash tabs and output panels will be restored to their default state. Continue?
encoder.dialog.title = Encode/Decode/Hash

encoder.morse.error = Input contains one or more characters which can't be converted.

encoder.name = Encoder Addon

encoder.optionspanel.base64 = Base64
Expand All @@ -30,7 +32,6 @@ encoder.optionspanel.name = Encode/Decode
encoder.popup.delete = Delete Output Panel
encoder.popup.replace.input = Replace Input Text
encoder.popup.title = Encode/Decode/Hash...

encoder.predefined.ascify = ASCify (Strip accents, etc)
encoder.predefined.base64decode = Base64 Decode
encoder.predefined.base64encode = Base64 Encode
Expand All @@ -50,6 +51,9 @@ encoder.predefined.javascriptdecode = JavaScript Decode
encoder.predefined.javascriptencode = JavaScript Encode
encoder.predefined.lowercase = To Lower Case
encoder.predefined.md5hash = MD5 Hash

encoder.predefined.morsecodedecode = Morse Code Decoder
encoder.predefined.morsecodeencode = Morse Code Encoder
encoder.predefined.powershellencode = PowerShell Encode
encoder.predefined.removewhitespace = Remove Whitespace
encoder.predefined.reverse = Reverse
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2022 The ZAP Development Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zaproxy.addon.encoder.processors.predefined;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.zaproxy.addon.encoder.ExtensionEncoder;
import org.zaproxy.addon.encoder.processors.EncodeDecodeResult;

class MorseDecoderUnitTest extends ProcessorTests<MorseDecoder> {

@BeforeAll
static void setup() {
mockMessages(new ExtensionEncoder());
}

@Override
protected MorseDecoder createProcessor() {
return MorseDecoder.getSingleton();
}

@ParameterizedTest
@ValueSource(
strings = {
"... --- .../... --- ...",
// em dashes not hyphens
"... ——— .../... ——— ..."
})
void shouldDecodeWithoutError(String input) throws Exception {
// Given / When
EncodeDecodeResult result = processor.process(input);
// Then
assertThat(result.hasError(), is(equalTo(false)));
assertThat(result.getResult(), is(equalTo("SOS SOS")));
}

@Test
void shouldErrorIfInputContainsOutOfScopeCharacter() throws Exception {
// Given / When
EncodeDecodeResult result = processor.process("abc");
// Then
assertThat(result.hasError(), is(equalTo(true)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2022 The ZAP Development Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zaproxy.addon.encoder.processors.predefined;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.zaproxy.addon.encoder.ExtensionEncoder;
import org.zaproxy.addon.encoder.processors.EncodeDecodeResult;

class MorseEncoderUnitTest extends ProcessorTests<MorseEncoder> {

@BeforeAll
static void setup() {
mockMessages(new ExtensionEncoder());
}

@Override
protected MorseEncoder createProcessor() {
return MorseEncoder.getSingleton();
}

@ParameterizedTest
@ValueSource(strings = {"SOS SOS", "SOS sos"})
void shouldEncodeWithoutError(String input) throws Exception {
// Given / When
EncodeDecodeResult result = processor.process(input);
// Then
assertThat(result.hasError(), is(equalTo(false)));
assertThat(result.getResult(), is(equalTo("... --- .../... --- ...")));
}

@Test
void shouldErrorIfInputContainsOutOfScopeCharacter() throws Exception {
// Given / When
EncodeDecodeResult result = processor.process("s*s");
// Then
assertThat(result.hasError(), is(equalTo(true)));
}
}
Loading