unisec 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/bin/unisec +7 -0
- data/lib/unisec/cli/cli.rb +27 -0
- data/lib/unisec/cli/confusables.rb +63 -0
- data/lib/unisec/cli/hexdump.rb +35 -0
- data/lib/unisec/cli/properties.rb +84 -0
- data/lib/unisec/cli/surrogates.rb +67 -0
- data/lib/unisec/confusables.rb +55 -0
- data/lib/unisec/hexdump.rb +104 -0
- data/lib/unisec/properties.rb +178 -0
- data/lib/unisec/surrogates.rb +119 -0
- data/lib/unisec/utils.rb +103 -0
- data/lib/unisec/version.rb +6 -0
- data/lib/unisec.rb +8 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1496d192c32a345de077d1643d041f15f960095f4931165a19b1250a52c5897e
|
4
|
+
data.tar.gz: 553ce1f9fa9d21895d31c144dc15fc73483a7301ececa1046138eec9b51a8707
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ae33d34f6bdf6ae0c5a3dd97ffdb3beaf9b2b7a3d3d48697502b272a8db4fc17a6f91bec5596b12c83d7cc609f0b3f7c9a80b7da09d5584537388868364810a5
|
7
|
+
data.tar.gz: c440d1868a5a97a8d6a126c66541d5627ea039086f85d90347dcb52910c32ebc9d1c97f28ce3e0fef62b209f0c982e24414edce98a32406bbc1f802f42bd8ef4
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 Alexandre ZANNI at ACCEIS
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/bin/unisec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'unisec/cli/surrogates'
|
4
|
+
require 'unisec/cli/hexdump'
|
5
|
+
require 'unisec/cli/properties'
|
6
|
+
require 'unisec/cli/confusables'
|
7
|
+
|
8
|
+
module Unisec
|
9
|
+
# Module used to create the CLI for the executable
|
10
|
+
module CLI
|
11
|
+
# Registered commands for the CLI
|
12
|
+
module Commands
|
13
|
+
extend Dry::CLI::Registry
|
14
|
+
|
15
|
+
# Mapping between the (sub-)commands as seen by the user
|
16
|
+
# on the command-line interface and the CLI modules in the lib
|
17
|
+
register 'surrogates to', Surrogates::To
|
18
|
+
register 'surrogates from', Surrogates::From
|
19
|
+
register 'hexdump', Hexdump
|
20
|
+
register 'properties list', Properties::List
|
21
|
+
register 'properties codepoints', Properties::Codepoints
|
22
|
+
register 'properties char', Properties::Char
|
23
|
+
register 'confusables list', Confusables::List
|
24
|
+
register 'confusables randomize', Confusables::Randomize
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/cli'
|
4
|
+
require 'unisec'
|
5
|
+
require 'unisec/utils'
|
6
|
+
|
7
|
+
module Unisec
|
8
|
+
module CLI
|
9
|
+
module Commands
|
10
|
+
# CLI sub-commands `unisec confusables xxx` for the class {Unisec::Confusables} from the lib.
|
11
|
+
module Confusables
|
12
|
+
# Command `unisec confusables list`
|
13
|
+
#
|
14
|
+
# Example:
|
15
|
+
#
|
16
|
+
# ```plaintext
|
17
|
+
# $ unisec confusables list '!'
|
18
|
+
# U+FF01 ! FULLWIDTH EXCLAMATION MARK
|
19
|
+
# U+01C3 ǃ LATIN LETTER RETROFLEX CLICK
|
20
|
+
# …
|
21
|
+
# ```
|
22
|
+
class List < Dry::CLI::Command
|
23
|
+
desc 'List confusables characters for a given character'
|
24
|
+
|
25
|
+
argument :character, required: true, desc: 'Unicode code point (as string)'
|
26
|
+
option :map, default: true, values: %w[true false],
|
27
|
+
desc: 'Allows partial mapping, includes confusable where the given chart is a part of'
|
28
|
+
|
29
|
+
# List confusables characters for a given character
|
30
|
+
# @param character [String] the character to search confusables for
|
31
|
+
# @option options [Boolean] :map allows partial mapping, includes confusable where the given chart is a
|
32
|
+
# part of
|
33
|
+
def call(character: nil, **options)
|
34
|
+
to_bool = ->(str) { ['true', true].include?(str) }
|
35
|
+
Unisec::Confusables.list_display(character, map: to_bool.call(options.fetch(:map)))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Command `unisec confusables randomize`
|
40
|
+
#
|
41
|
+
# Example:
|
42
|
+
#
|
43
|
+
# ```plaintext
|
44
|
+
# $ unisec confusables randomize noraj
|
45
|
+
# Original: noraj
|
46
|
+
# Transformed: ռ໐𝘳𝜶𝙟
|
47
|
+
# …
|
48
|
+
# ```
|
49
|
+
class Randomize < Dry::CLI::Command
|
50
|
+
desc 'Replace all characters from a string with random confusables when possible'
|
51
|
+
|
52
|
+
argument :str, required: true, desc: 'Unicode string'
|
53
|
+
|
54
|
+
# Replace all characters from a string with random confusables when possible
|
55
|
+
# @param str [String] Unicode string
|
56
|
+
def call(str: nil, **)
|
57
|
+
Unisec::Confusables.randomize_display(str)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/cli'
|
4
|
+
require 'unisec'
|
5
|
+
|
6
|
+
module Unisec
|
7
|
+
module CLI
|
8
|
+
module Commands
|
9
|
+
# CLI command `unisec hexdumps` for the class {Unisec::Hexdump} from the lib.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# ```plaintext
|
14
|
+
# $ unisec hexdump "ACCEIS"
|
15
|
+
# UTF-8: 41 43 43 45 49 53
|
16
|
+
# UTF-16BE: 0041 0043 0043 0045 0049 0053
|
17
|
+
# UTF-16LE: 4100 4300 4300 4500 4900 5300
|
18
|
+
# UTF-32BE: 00000041 00000043 00000043 00000045 00000049 00000053
|
19
|
+
# UTF-32LE: 41000000 43000000 43000000 45000000 49000000 53000000
|
20
|
+
# ```
|
21
|
+
class Hexdump < Dry::CLI::Command
|
22
|
+
desc 'Hexdump in all Unicode encodings'
|
23
|
+
|
24
|
+
argument :input, required: true,
|
25
|
+
desc: 'String input'
|
26
|
+
|
27
|
+
# Hexdump of all Unicode encodings.
|
28
|
+
# @param input [String] Input string to encode
|
29
|
+
def call(input: nil, **)
|
30
|
+
puts Unisec::Hexdump.new(input).display
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/cli'
|
4
|
+
require 'unisec'
|
5
|
+
require 'unisec/utils'
|
6
|
+
|
7
|
+
module Unisec
|
8
|
+
module CLI
|
9
|
+
module Commands
|
10
|
+
# CLI sub-commands `unisec properties xxx` for the class {Unisec::Properties} from the lib.
|
11
|
+
module Properties
|
12
|
+
# Command `unisec properties list`
|
13
|
+
#
|
14
|
+
# Example:
|
15
|
+
#
|
16
|
+
# ```plaintext
|
17
|
+
# $ unisec properties list
|
18
|
+
# ASCII_Hex_Digit
|
19
|
+
# Age
|
20
|
+
# Alphabetic
|
21
|
+
# …
|
22
|
+
# ```
|
23
|
+
class List < Dry::CLI::Command
|
24
|
+
desc 'List all Unicode properties'
|
25
|
+
|
26
|
+
# List Unicode properties name
|
27
|
+
def call(**)
|
28
|
+
Unisec::Properties.list.each do |p|
|
29
|
+
puts p
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Command `unisec properties codepoints`
|
35
|
+
#
|
36
|
+
# Example:
|
37
|
+
#
|
38
|
+
# ```plaintext
|
39
|
+
# $ unisec properties codepoints Bidi_Control
|
40
|
+
# U+61C ARABIC LETTER MARK
|
41
|
+
# …
|
42
|
+
# ```
|
43
|
+
class Codepoints < Dry::CLI::Command
|
44
|
+
desc 'List all code points for a given property'
|
45
|
+
|
46
|
+
argument :property, required: true, desc: 'Unicode property name'
|
47
|
+
|
48
|
+
# List code points matching a Unicode property
|
49
|
+
# @param property [String] property name
|
50
|
+
def call(property: nil, **)
|
51
|
+
Unisec::Properties.codepoints_display(property)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Command `unisec properties char`
|
56
|
+
#
|
57
|
+
# Example:
|
58
|
+
#
|
59
|
+
# ```plaintext
|
60
|
+
# $ unisec properties char é
|
61
|
+
# Name: LATIN SMALL LETTER E WITH ACUTE
|
62
|
+
# Code Point: U+00E9
|
63
|
+
#
|
64
|
+
# Block: Latin-1 Supplement
|
65
|
+
# …
|
66
|
+
# ```
|
67
|
+
class Char < Dry::CLI::Command
|
68
|
+
desc 'Returns all properties of a given Unicode character (code point as string)'
|
69
|
+
|
70
|
+
argument :character, required: true, desc: 'Unicode character'
|
71
|
+
option :extended, default: false, values: %w[true false], desc: 'Show all properties'
|
72
|
+
|
73
|
+
# Returns all properties of a given Unicode character (code point as string)
|
74
|
+
# @param character [String] Unicode code point (as character / string)
|
75
|
+
# @option options [Boolean] :extended Show all properties
|
76
|
+
def call(character: nil, **options)
|
77
|
+
to_bool = ->(str) { str == 'true' }
|
78
|
+
Unisec::Properties.char_display(character, extended: to_bool.call(options.fetch(:extended)))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/cli'
|
4
|
+
require 'unisec'
|
5
|
+
require 'unisec/utils'
|
6
|
+
|
7
|
+
module Unisec
|
8
|
+
module CLI
|
9
|
+
module Commands
|
10
|
+
# CLI sub-commands `unisec surrogates xxx` for the class {Unisec::Surrogates} from the lib.
|
11
|
+
module Surrogates
|
12
|
+
# Command `unisec surrogates from`
|
13
|
+
#
|
14
|
+
# Example:
|
15
|
+
#
|
16
|
+
# ```plaintext
|
17
|
+
# $ unisec surrogates from 0xD801 0xDC37
|
18
|
+
# Char: 𐐷
|
19
|
+
# Code Point: 0x10437, 0d66615, 0b10000010000110111
|
20
|
+
# High Surrogate: 0xD801, 0d55297, 0b1101100000000001
|
21
|
+
# Low Surrogate: 0xDC37, 0d56375, 0b1101110000110111
|
22
|
+
# ```
|
23
|
+
class From < Dry::CLI::Command
|
24
|
+
desc 'Code point ⬅️ Surrogates'
|
25
|
+
|
26
|
+
argument :high, required: true,
|
27
|
+
desc: 'High surrogate (in hexadecimal (0xXXXX), decimal (0dXXXX), binary (0bXXXX) or as text)'
|
28
|
+
argument :low, required: true,
|
29
|
+
desc: 'Low surrogate (in hexadecimal (0xXXXX), decimal (0dXXXX), binary (0bXXXX) or as text)'
|
30
|
+
|
31
|
+
# Calculate the Unicode code point based on the surrogates.
|
32
|
+
# @param high [String] decimal high surrogate
|
33
|
+
# @param low [String] decimal low surrogate
|
34
|
+
def call(high: nil, low: nil, **)
|
35
|
+
puts Unisec::Surrogates.new(Unisec::Utils::String.convert(high, :integer),
|
36
|
+
Unisec::Utils::String.convert(low, :integer)).display
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Command `unisec surrogates to`
|
41
|
+
#
|
42
|
+
# Example:
|
43
|
+
#
|
44
|
+
# ```plaintext
|
45
|
+
# $ unisec surrogates to 0x1F4A9
|
46
|
+
# Char: 💩
|
47
|
+
# Code Point: 0x1F4A9, 0d128169, 0b11111010010101001
|
48
|
+
# High Surrogate: 0xD83D, 0d55357, 0b1101100000111101
|
49
|
+
# Low Surrogate: 0xDCA9, 0d56489, 0b1101110010101001
|
50
|
+
# ```
|
51
|
+
class To < Dry::CLI::Command
|
52
|
+
desc 'Code point ➡️ Surrogates'
|
53
|
+
|
54
|
+
argument :codepoint, required: true,
|
55
|
+
desc: 'One code point (character) (in hexadecimal (0xXXXX), decimal (0dXXXX), binary ' \
|
56
|
+
'(0bXXXX) or as text)'
|
57
|
+
|
58
|
+
# Calculate the surrogates based on the Unicode code point.
|
59
|
+
# @param codepoint [String] decimal codepoint
|
60
|
+
def call(codepoint: nil, **)
|
61
|
+
puts Unisec::Surrogates.new(Unisec::Utils::String.convert(codepoint, :integer)).display
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'unicode/confusable'
|
4
|
+
require 'twitter_cldr'
|
5
|
+
|
6
|
+
module Unisec
|
7
|
+
# Operations about Unicode confusable characters (homoglyphs).
|
8
|
+
class Confusables
|
9
|
+
# List confusables characters for a given character
|
10
|
+
# @param chr [String] the character to search confusables for
|
11
|
+
# @param map [Boolean] allows partial mapping, includes confusable where the given chart is a part of
|
12
|
+
# @return [Array<String>] list of confusables
|
13
|
+
# @example
|
14
|
+
# Unisec::Confusables.list('!') # => ["!", "ǃ", "ⵑ", "‼", "⁉", "⁈"]
|
15
|
+
# Unisec::Confusables.list('!', map: false) # => ["!", "ǃ", "ⵑ"]
|
16
|
+
def self.list(chr, map: true)
|
17
|
+
Unicode::Confusable.list(chr, map)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Display a CLI-friendly output listing all confusables corresponding to a character (code point)
|
21
|
+
# @param chr [String] the character to search confusables for
|
22
|
+
# @param map [Boolean] allows partial mapping, includes confusable where the given chart is a part of
|
23
|
+
def self.list_display(chr, map: true)
|
24
|
+
Confusables.list(chr, map: map).each do |confu|
|
25
|
+
puts "#{Properties.char2codepoint(confu).ljust(9)} #{confu.ljust(4)} " \
|
26
|
+
"#{TwitterCldr::Shared::CodePoint.get(confu.codepoints.first).name}"
|
27
|
+
end
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
# Replace all characters with random confusables when possible.
|
32
|
+
# @param str [String] Unicode string
|
33
|
+
# @return [String] input randomized with confusables
|
34
|
+
# @example
|
35
|
+
# Unisec::Confusables.randomize('noraj') # => "𝓃ⲟ𝓇𝒶j"
|
36
|
+
# Unisec::Confusables.randomize('noraj') # => "𝗻૦𝚛⍺𝐣"
|
37
|
+
# Unisec::Confusables.randomize('noraj') # => "𝔫𞺄𝕣⍺j"
|
38
|
+
def self.randomize(str)
|
39
|
+
out = ''
|
40
|
+
str.each_char do |chr|
|
41
|
+
confu = Confusables.list(chr, map: false).sample
|
42
|
+
out += confu.nil? ? chr : confu
|
43
|
+
end
|
44
|
+
out
|
45
|
+
end
|
46
|
+
|
47
|
+
# Display a CLI-friendly output of a string where characters are replaces with random confusables
|
48
|
+
# @param str [String] Unicode string
|
49
|
+
def self.randomize_display(str)
|
50
|
+
display = ->(key, value) { puts Paint[key, :red, :bold].ljust(23) + " #{value}" }
|
51
|
+
display.call('Original:', str)
|
52
|
+
display.call('Transformed:', Unisec::Confusables.randomize(str))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ctf_party'
|
4
|
+
|
5
|
+
module Unisec
|
6
|
+
# Hexdump of all Unicode encodings.
|
7
|
+
class Hexdump
|
8
|
+
# UTF-8 hexdump
|
9
|
+
# @return [String] UTF-8 hexdump
|
10
|
+
attr_reader :utf8
|
11
|
+
|
12
|
+
# UTF-16BE hexdump
|
13
|
+
# @return [String] UTF-16BE hexdump
|
14
|
+
attr_reader :utf16be
|
15
|
+
|
16
|
+
# UTF-16LE hexdump
|
17
|
+
# @return [String] UTF-16LE hexdump
|
18
|
+
attr_reader :utf16le
|
19
|
+
|
20
|
+
# UTF-32BE hexdump
|
21
|
+
# @return [String] UTF-32BE hexdump
|
22
|
+
attr_reader :utf32be
|
23
|
+
|
24
|
+
# UTF-32LE hexdump
|
25
|
+
# @return [String] UTF-32LE hexdump
|
26
|
+
attr_reader :utf32le
|
27
|
+
|
28
|
+
# Init the hexdump.
|
29
|
+
# @param str [String] Input string to encode
|
30
|
+
# @example
|
31
|
+
# hxd = Unisec::Hexdump.new('I 💕 Ruby 💎')
|
32
|
+
# hxd.utf8 # => "49 20 f0 9f 92 95 20 52 75 62 79 20 f0 9f 92 8e"
|
33
|
+
# hxd.utf16be # => "0049 0020 d83d dc95 0020 0052 0075 0062 0079 0020 d83d dc8e"
|
34
|
+
# hxd.utf32be # => "00000049 00000020 0001f495 00000020 00000052 00000075 00000062 00000079 00000020 0001f48e"
|
35
|
+
def initialize(str)
|
36
|
+
@utf8 = Hexdump.utf8(str)
|
37
|
+
@utf16be = Hexdump.utf16be(str)
|
38
|
+
@utf16le = Hexdump.utf16le(str)
|
39
|
+
@utf32be = Hexdump.utf32be(str)
|
40
|
+
@utf32le = Hexdump.utf32le(str)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Encode to UTF-8 in hexdump format (spaced at every code unit = every byte)
|
44
|
+
# @param str [String] Input string to encode
|
45
|
+
# @return [String] hexdump (UTF-8 encoded)
|
46
|
+
# @example
|
47
|
+
# Unisec::Hexdump.utf8('🐋') # => "f0 9f 90 8b"
|
48
|
+
def self.utf8(str)
|
49
|
+
str.encode('UTF-8').to_hex.scan(/.{2}/).join(' ')
|
50
|
+
end
|
51
|
+
|
52
|
+
# Encode to UTF-16BE in hexdump format (spaced at every code unit = every 2 bytes)
|
53
|
+
# @param str [String] Input string to encode
|
54
|
+
# @return [String] hexdump (UTF-16BE encoded)
|
55
|
+
# @example
|
56
|
+
# Unisec::Hexdump.utf16be('🐋') # => "d83d dc0b"
|
57
|
+
def self.utf16be(str)
|
58
|
+
str.encode('UTF-16BE').to_hex.scan(/.{4}/).join(' ')
|
59
|
+
end
|
60
|
+
|
61
|
+
# Encode to UTF-16LE in hexdump format (spaced at every code unit = every 2 bytes)
|
62
|
+
# @param str [String] Input string to encode
|
63
|
+
# @return [String] hexdump (UTF-16LE encoded)
|
64
|
+
# @example
|
65
|
+
# Unisec::Hexdump.utf16le('🐋') # => "3dd8 0bdc"
|
66
|
+
def self.utf16le(str)
|
67
|
+
str.encode('UTF-16LE').to_hex.scan(/.{4}/).join(' ')
|
68
|
+
end
|
69
|
+
|
70
|
+
# Encode to UTF-32BE in hexdump format (spaced at every code unit = every 4 bytes)
|
71
|
+
# @param str [String] Input string to encode
|
72
|
+
# @return [String] hexdump (UTF-32BE encoded)
|
73
|
+
# @example
|
74
|
+
# Unisec::Hexdump.utf32be('🐋') # => "0001f40b"
|
75
|
+
def self.utf32be(str)
|
76
|
+
str.encode('UTF-32BE').to_hex.scan(/.{8}/).join(' ')
|
77
|
+
end
|
78
|
+
|
79
|
+
# Encode to UTF-32LE in hexdump format (spaced at every code unit = every 4 bytes)
|
80
|
+
# @param str [String] Input string to encode
|
81
|
+
# @return [String] hexdump (UTF-32LE encoded)
|
82
|
+
# @example
|
83
|
+
# Unisec::Hexdump.utf32le('🐋') # => "0bf40100"
|
84
|
+
def self.utf32le(str)
|
85
|
+
str.encode('UTF-32LE').to_hex.scan(/.{8}/).join(' ')
|
86
|
+
end
|
87
|
+
|
88
|
+
# Display a CLI-friendly output summurizing the hexdump in all Unicode encodings
|
89
|
+
# @example
|
90
|
+
# puts Unisec::Hexdump.new('K').display # =>
|
91
|
+
# # UTF-8: e2 84 aa
|
92
|
+
# # UTF-16BE: 212a
|
93
|
+
# # UTF-16LE: 2a21
|
94
|
+
# # UTF-32BE: 0000212a
|
95
|
+
# # UTF-32LE: 2a210000
|
96
|
+
def display
|
97
|
+
"UTF-8: #{@utf8}\n" \
|
98
|
+
"UTF-16BE: #{@utf16be}\n" \
|
99
|
+
"UTF-16LE: #{@utf16le}\n" \
|
100
|
+
"UTF-32BE: #{@utf32be}\n" \
|
101
|
+
"UTF-32LE: #{@utf32le}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'twitter_cldr'
|
4
|
+
require 'paint'
|
5
|
+
|
6
|
+
module Unisec
|
7
|
+
# Manipulate Unicode properties
|
8
|
+
class Properties
|
9
|
+
# List Unicode properties name
|
10
|
+
# @return [Array<String>] properties name
|
11
|
+
# @example
|
12
|
+
# Unisec::Properties.list # => ["ASCII_Hex_Digit", "Age", "Alphabetic", … ]
|
13
|
+
def self.list
|
14
|
+
TwitterCldr::Shared::CodePoint.properties.property_names
|
15
|
+
end
|
16
|
+
|
17
|
+
# List all code points for a given property
|
18
|
+
# @param prop [String] the property name
|
19
|
+
# @return [Array<Hash>] Array of code points (`{char: String, codepoint: Integer, name: String}`)
|
20
|
+
# @example
|
21
|
+
# Unisec::Properties.codepoints('Quotation_Mark')
|
22
|
+
# # =>
|
23
|
+
# # [{:char=>"\"", :codepoint=>34, :name=>"QUOTATION MARK"},
|
24
|
+
# # {:char=>"'", :codepoint=>39, :name=>"APOSTROPHE"},
|
25
|
+
# # … ]
|
26
|
+
def self.codepoints(prop)
|
27
|
+
cp = TwitterCldr::Shared::CodePoint
|
28
|
+
out = []
|
29
|
+
ranges = cp.properties.code_points_for_property(prop).ranges
|
30
|
+
ranges.each do |range|
|
31
|
+
range.each do |i|
|
32
|
+
codepoint = cp.get(i)
|
33
|
+
out << {
|
34
|
+
char: TwitterCldr::Utils::CodePoints.to_string([codepoint.code_point]),
|
35
|
+
codepoint: codepoint.code_point,
|
36
|
+
name: codepoint.name
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
out
|
41
|
+
end
|
42
|
+
|
43
|
+
# Display a CLI-friendly output listing all code points corresponding to a property.
|
44
|
+
# @example
|
45
|
+
# Unisec::Properties.codepoints_display('Quotation_Mark')
|
46
|
+
# # =>
|
47
|
+
# # U+0022 " QUOTATION MARK
|
48
|
+
# # U+0027 ' APOSTROPHE
|
49
|
+
# # …
|
50
|
+
def self.codepoints_display(prop)
|
51
|
+
codepoints = Properties.codepoints(prop)
|
52
|
+
codepoints.each do |cp|
|
53
|
+
puts "#{Properties.char2codepoint(cp[:char]).ljust(7)} #{cp[:char].ljust(4)} #{cp[:name]}"
|
54
|
+
end
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns all properties of a given unicode character (code point)
|
59
|
+
# @param chr [String] Unicode code point (as character / string)
|
60
|
+
# @return [Hash] All properties of the given code point
|
61
|
+
# @example
|
62
|
+
# Unisec::Properties.char('é')
|
63
|
+
# # =>
|
64
|
+
# # {:age=>"1.1",
|
65
|
+
# # … }
|
66
|
+
def self.char(chr)
|
67
|
+
cp_num = TwitterCldr::Utils::CodePoints.from_string(chr)
|
68
|
+
cp = TwitterCldr::Shared::CodePoint.get(cp_num.first)
|
69
|
+
props = cp.properties
|
70
|
+
props_hash = props.properties_hash.dup
|
71
|
+
%w[Age Block General_Category Script].each { |p| props_hash.delete(p) } # Remaining properties
|
72
|
+
categories = props.general_category.map do |cat|
|
73
|
+
TwitterCldr::Shared::PropertyValueAliases.long_alias_for('gc', cat)
|
74
|
+
end
|
75
|
+
{
|
76
|
+
age: props.age.join,
|
77
|
+
block: props.block.join,
|
78
|
+
category: categories[1],
|
79
|
+
subcategory: categories[0],
|
80
|
+
codepoint: Properties.char2codepoint(chr),
|
81
|
+
name: cp.name,
|
82
|
+
script: props.script.join,
|
83
|
+
case: {
|
84
|
+
ruby: {
|
85
|
+
lowercase: chr.downcase,
|
86
|
+
uppercase: chr.upcase
|
87
|
+
},
|
88
|
+
twitter: {
|
89
|
+
lowercase: chr.localize.downcase.to_s,
|
90
|
+
uppercase: chr.localize.upcase.to_s,
|
91
|
+
titlecase: chr.localize.titlecase.to_s,
|
92
|
+
casefold: chr.localize.casefold.to_s
|
93
|
+
}
|
94
|
+
},
|
95
|
+
normalization: {
|
96
|
+
ruby: {
|
97
|
+
nfkd: chr.unicode_normalize(:nfkd),
|
98
|
+
nfkc: chr.unicode_normalize(:nfkc),
|
99
|
+
nfd: chr.unicode_normalize(:nfd),
|
100
|
+
nfc: chr.unicode_normalize(:nfc)
|
101
|
+
},
|
102
|
+
twitter: {
|
103
|
+
nfkd: chr.localize.normalize(using: :NFKD).to_s,
|
104
|
+
nfkc: chr.localize.normalize(using: :NFKC).to_s,
|
105
|
+
nfd: chr.localize.normalize(using: :NFD).to_s,
|
106
|
+
nfc: chr.localize.normalize(using: :NFC).to_s
|
107
|
+
}
|
108
|
+
},
|
109
|
+
other_properties: props_hash
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
# Display a CLI-friendly output listing all properties corresponding to character (code point)
|
114
|
+
# @param chr [String] Unicode code point (as character / string)
|
115
|
+
# @param extended [String] By default, it will only show common properties, with extended set to `true` it will
|
116
|
+
# show all of them.
|
117
|
+
def self.char_display(chr, extended: false)
|
118
|
+
data = Properties.char(chr)
|
119
|
+
display = ->(key, value) { puts Paint[key, :red, :bold].ljust(30) + " #{value}" }
|
120
|
+
display.call('Name:', data[:name])
|
121
|
+
display.call('Code Point:', data[:codepoint])
|
122
|
+
puts
|
123
|
+
display.call('Block:', data[:block])
|
124
|
+
display.call('Category:', data[:category])
|
125
|
+
display.call('Sub-Category:', data[:subcategory])
|
126
|
+
display.call('Script:', data[:script])
|
127
|
+
display.call('Since (age):', "Version #{data[:age]}")
|
128
|
+
puts
|
129
|
+
x = data.dig(:case, :twitter, :uppercase)
|
130
|
+
display.call('Uppercase:', x + " (#{Properties.char2codepoint(x)})")
|
131
|
+
x = data.dig(:case, :twitter, :lowercase)
|
132
|
+
display.call('Lowercase:', x + " (#{Properties.char2codepoint(x)})")
|
133
|
+
x = data.dig(:case, :twitter, :titlecase)
|
134
|
+
display.call('Titlecase:', x + " (#{Properties.char2codepoint(x)})")
|
135
|
+
x = data.dig(:case, :twitter, :casefold)
|
136
|
+
display.call('Casefold:', x + " (#{Properties.char2codepoint(x)})")
|
137
|
+
puts
|
138
|
+
x = data.dig(:normalization, :twitter, :nfkd)
|
139
|
+
display.call('Normalization NFKD:', x + " (#{Properties.chars2codepoints(x)})")
|
140
|
+
x = data.dig(:normalization, :twitter, :nfkc)
|
141
|
+
display.call('Normalization NFKC:', x + " (#{Properties.chars2codepoints(x)})")
|
142
|
+
x = data.dig(:normalization, :twitter, :nfd)
|
143
|
+
display.call('Normalization NFD:', x + " (#{Properties.chars2codepoints(x)})")
|
144
|
+
x = data.dig(:normalization, :twitter, :nfc)
|
145
|
+
display.call('Normalization NFC:', x + " (#{Properties.chars2codepoints(x)})")
|
146
|
+
if extended
|
147
|
+
puts
|
148
|
+
data[:other_properties].each do |k, v|
|
149
|
+
display.call(k, v&.join)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
nil
|
153
|
+
end
|
154
|
+
|
155
|
+
# Display the code point in Unicode format for a given character (code point as string)
|
156
|
+
# @param chr [String] Unicode code point (as character / string)
|
157
|
+
# @return [String] code point in Unicode format
|
158
|
+
# @example
|
159
|
+
# Unisec::Properties.char2codepoint('💎') # => "U+1F48E"
|
160
|
+
def self.char2codepoint(chr)
|
161
|
+
"U+#{format('%.4x', chr.codepoints.first).upcase}"
|
162
|
+
end
|
163
|
+
|
164
|
+
# Display the code points in Unicode format for the given characters (code points as string)
|
165
|
+
# @param chrs [String] Unicode code points (as characters / string)
|
166
|
+
# @return [String] code points in Unicode format
|
167
|
+
# @example
|
168
|
+
# Unisec::Properties.chars2codepoints("ỳ́") # => "U+0079 U+0300 U+0301"
|
169
|
+
# Unisec::Properties.chars2codepoints("🧑🌾") # => "U+1F9D1 U+200D U+1F33E"
|
170
|
+
def self.chars2codepoints(chrs)
|
171
|
+
out = []
|
172
|
+
chrs.each_char do |chr|
|
173
|
+
out << Properties.char2codepoint(chr)
|
174
|
+
end
|
175
|
+
out.join(' ')
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'unisec/utils'
|
4
|
+
require 'ctf_party'
|
5
|
+
|
6
|
+
module Unisec
|
7
|
+
# UTF-16 surrogates conversion.
|
8
|
+
class Surrogates
|
9
|
+
# Unicode code point
|
10
|
+
# @return [Integer] decimal codepoint
|
11
|
+
attr_reader :cp
|
12
|
+
|
13
|
+
# High surrogate (1st code unit of a surrogate pair). Also called lead surrogate.
|
14
|
+
# @return [Integer] decimal high surrogate
|
15
|
+
attr_reader :hs
|
16
|
+
|
17
|
+
# Low surrogate (2nd code unit of a surrogate pair). Also called trail surrogate.
|
18
|
+
# @return [Integer] decimal low surrogate
|
19
|
+
attr_reader :ls
|
20
|
+
|
21
|
+
# Init the surrogate pair.
|
22
|
+
# @param args [Integer] If one argument is provided, it's evaluated as the
|
23
|
+
# code point and the two surrogates will be calculated automatically.
|
24
|
+
# If two arguments are provided, they are evaluated as a surrogate pair (high
|
25
|
+
# then low) and the code point will be calculated.
|
26
|
+
# @example
|
27
|
+
# surr = Unisec::Surrogates.new(128169)
|
28
|
+
# # => #<Unisec::Surrogates:0x00007f96920a7ca8 @cp=128169, @hs=55357, @ls=56489>
|
29
|
+
# surr.cp # => 128169
|
30
|
+
# surr.hs # => 55357
|
31
|
+
# surr.ls # => 56489
|
32
|
+
# Unisec::Surrogates.new(55357, 56489)
|
33
|
+
# # => #<Unisec::Surrogates:0x00007f96920689b8 @cp=128169, @hs=55357, @ls=56489>
|
34
|
+
def initialize(*args)
|
35
|
+
if args.size == 1
|
36
|
+
@cp = args[0]
|
37
|
+
@hs = high_surrogate
|
38
|
+
@ls = low_surrogate
|
39
|
+
elsif args.size == 2
|
40
|
+
@hs = args[0]
|
41
|
+
@ls = args[1]
|
42
|
+
@cp = code_point
|
43
|
+
else
|
44
|
+
raise ArgumentError
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Calculate the high surrogate based on the Unicode code point.
|
49
|
+
# @param codepoint [Integer] decimal codepoint
|
50
|
+
# @return [Integer] decimal high surrogate
|
51
|
+
# @example
|
52
|
+
# Unisec::Surrogates.high_surrogate(128169) # => 55357
|
53
|
+
def self.high_surrogate(codepoint)
|
54
|
+
(((codepoint - 0x10000) / 0x400).floor + 0xd800)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Calculate the low surrogate based on the Unicode code point.
|
58
|
+
# @param codepoint [Integer] decimal codepoint
|
59
|
+
# @return [Integer] decimal low surrogate
|
60
|
+
# @example
|
61
|
+
# Unisec::Surrogates.low_surrogate(128169) # => 56489
|
62
|
+
def self.low_surrogate(codepoint)
|
63
|
+
(((codepoint - 0x10000) % 0x400) + 0xdc00)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Calculate the Unicode code point based on the surrogates.
|
67
|
+
# @param hs [Integer] decimal high surrogate
|
68
|
+
# @param ls [Integer] decimal low surrogate
|
69
|
+
# @return [Integer] decimal code point
|
70
|
+
# @example
|
71
|
+
# Unisec::Surrogates.code_point(55357, 56489) # => 128169
|
72
|
+
def self.code_point(hs, ls)
|
73
|
+
(((hs - 0xd800) * 0x400) + ls - 0xdc00 + 0x10000)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Same as accessing {.hs}. Calculate the {.high_surrogate}.
|
77
|
+
# @return [Integer] decimal high surrogate
|
78
|
+
# @example
|
79
|
+
# surr = Unisec::Surrogates.new(128169)
|
80
|
+
# surr.high_surrogate # => 55357
|
81
|
+
def high_surrogate
|
82
|
+
@hs = Surrogates.high_surrogate(@cp)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Same as accessing {.ls}. Calculate the {.low_surrogate}.
|
86
|
+
# @return [Integer] decimal low surrogate
|
87
|
+
# @example
|
88
|
+
# surr = Unisec::Surrogates.new(128169)
|
89
|
+
# surr.low_surrogate # => 56489
|
90
|
+
def low_surrogate
|
91
|
+
@ls = Surrogates.low_surrogate(@cp)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Same as accessing {.cp}. Calculate the {.code_point}.
|
95
|
+
# @return [Integer] decimal code point
|
96
|
+
# surr = Unisec::Surrogates.new(55357, 56489)
|
97
|
+
# surr.code_point # => 128169
|
98
|
+
def code_point
|
99
|
+
@cp = Surrogates.code_point(@hs, @ls)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Display a CLI-friendly output summurizing everithing about the surrogates:
|
103
|
+
# the corresponding character, code point, high and low surrogates
|
104
|
+
# (each displayed as hexadecimal, decimal and binary).
|
105
|
+
# @example
|
106
|
+
# surr = Unisec::Surrogates.new(128169)
|
107
|
+
# puts surr.display # =>
|
108
|
+
# # Char: 💩
|
109
|
+
# # Code Point: 0x1F4A9, 0d128169, 0b11111010010101001
|
110
|
+
# # High Surrogate: 0xD83D, 0d55357, 0b1101100000111101
|
111
|
+
# # Low Surrogate: 0xDCA9, 0d56489, 0b1101110010101001
|
112
|
+
def display
|
113
|
+
"Char: #{[@cp].pack('U*')}\n" \
|
114
|
+
"Code Point: 0x#{@cp.to_hex}, 0d#{@cp}, 0b#{@cp.to_bin}\n" \
|
115
|
+
"High Surrogate: 0x#{@hs.to_hex}, 0d#{@hs}, 0b#{@hs.to_bin}\n" \
|
116
|
+
"Low Surrogate: 0x#{@ls.to_hex}, 0d#{@ls}, 0b#{@ls.to_bin}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/lib/unisec/utils.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ctf_party'
|
4
|
+
|
5
|
+
class Integer
|
6
|
+
# Convert an integer to an hexadecimal string
|
7
|
+
# @return [String] The interger converted to hexadecimal and casted to an upper case string
|
8
|
+
# @example
|
9
|
+
# 42.to_hex # => "2A"
|
10
|
+
def to_hex
|
11
|
+
to_s(16).upcase
|
12
|
+
end
|
13
|
+
|
14
|
+
# Convert an integer to an binary string
|
15
|
+
# @return [String] The interger converted to binary and casted to a string
|
16
|
+
# @example
|
17
|
+
# 42.to_bin # => "101010"
|
18
|
+
def to_bin
|
19
|
+
to_s(2)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module Unisec
|
24
|
+
# Generic stuff not Unicode-related that can be re-used.
|
25
|
+
module Utils
|
26
|
+
# About string conversion and manipulation.
|
27
|
+
module String
|
28
|
+
# Convert a string input into the chosen type.
|
29
|
+
# @param input [String] If the target type is `:integer`, the string must represent a number encoded in
|
30
|
+
# hexadecimal, decimal, binary. If it's a Unicode string, only the first code point will be taken into account.
|
31
|
+
# @param target_type [Symbol] Convert to the chosen type. Currently only supports `:integer`.
|
32
|
+
# @return [Variable] The type of the output depends on the chosen `target_type`.
|
33
|
+
# @example
|
34
|
+
# Unisec::Utils::String.convert('0x1f4a9', :integer) # => 128169
|
35
|
+
def self.convert(input, target_type)
|
36
|
+
case target_type
|
37
|
+
when :integer
|
38
|
+
convert_to_integer(input)
|
39
|
+
else
|
40
|
+
raise TypeError, "Target type \"#{target_type}\" not avaible"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Internal method used for {.convert}.
|
45
|
+
#
|
46
|
+
# Convert a string input into integer.
|
47
|
+
# @param input [String] The string must represent a number encoded in hexadecimal, decimal, binary. If it's a
|
48
|
+
# Unicode string, only the first code point will be taken into account. The input type is determined
|
49
|
+
# automatically based on the prefix.
|
50
|
+
# @return [Integer]
|
51
|
+
# @example
|
52
|
+
# # Hexadecimal
|
53
|
+
# Unisec::Utils::String.convert_to_integer('0x1f4a9') # => 128169
|
54
|
+
# # Decimal
|
55
|
+
# Unisec::Utils::String.convert_to_integer('0d128169') # => 128169
|
56
|
+
# # Binary
|
57
|
+
# Unisec::Utils::String.convert_to_integer('0b11111010010101001') # => 128169
|
58
|
+
# # Unicode string
|
59
|
+
# Unisec::Utils::String.convert_to_integer('💩') # => 128169
|
60
|
+
def self.convert_to_integer(input)
|
61
|
+
case autodetect(input)
|
62
|
+
when :hexadecimal
|
63
|
+
input.hex2dec(prefix: '0x').to_i
|
64
|
+
when :decimal
|
65
|
+
input.to_i
|
66
|
+
when :binary
|
67
|
+
input.bin2hex.hex2dec.to_i
|
68
|
+
when :string
|
69
|
+
input.codepoints.first
|
70
|
+
else
|
71
|
+
raise TypeError, "Input \"#{input}\" is not of the expected type"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Internal method used for {.convert}.
|
76
|
+
#
|
77
|
+
# Autodetect the representation type of the string input.
|
78
|
+
# @param str [String] Input.
|
79
|
+
# @return [Symbol] the detected type: `:hexadecimal`, `:decimal`, `:binary`, `:string`.
|
80
|
+
# @example
|
81
|
+
# # Hexadecimal
|
82
|
+
# Unisec::Utils::String.autodetect('0x1f4a9') # => :hexadecimal
|
83
|
+
# # Decimal
|
84
|
+
# Unisec::Utils::String.autodetect('0d128169') # => :decimal
|
85
|
+
# # Binary
|
86
|
+
# Unisec::Utils::String.autodetect('0b11111010010101001') # => :binary
|
87
|
+
# # Unicode string
|
88
|
+
# Unisec::Utils::String.autodetect('💩') # => :string
|
89
|
+
def self.autodetect(str)
|
90
|
+
case str
|
91
|
+
when /0x[0-9a-fA-F]/
|
92
|
+
:hexadecimal
|
93
|
+
when /0d[0-9]+/
|
94
|
+
:decimal
|
95
|
+
when /0b[0-1]+/
|
96
|
+
:binary
|
97
|
+
else
|
98
|
+
:string
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/unisec.rb
ADDED
metadata
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: unisec
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexandre ZANNI
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-07-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ctf-party
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dry-cli
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: paint
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.3'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: twitter_cldr
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '6.11'
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 6.11.5
|
65
|
+
type: :runtime
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - "~>"
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '6.11'
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 6.11.5
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: unicode-confusable
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '1.9'
|
82
|
+
type: :runtime
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '1.9'
|
89
|
+
description: Toolkit for security research manipulating Unicode
|
90
|
+
email: alexandre.zanni@europe.com
|
91
|
+
executables:
|
92
|
+
- unisec
|
93
|
+
extensions: []
|
94
|
+
extra_rdoc_files: []
|
95
|
+
files:
|
96
|
+
- LICENSE
|
97
|
+
- bin/unisec
|
98
|
+
- lib/unisec.rb
|
99
|
+
- lib/unisec/cli/cli.rb
|
100
|
+
- lib/unisec/cli/confusables.rb
|
101
|
+
- lib/unisec/cli/hexdump.rb
|
102
|
+
- lib/unisec/cli/properties.rb
|
103
|
+
- lib/unisec/cli/surrogates.rb
|
104
|
+
- lib/unisec/confusables.rb
|
105
|
+
- lib/unisec/hexdump.rb
|
106
|
+
- lib/unisec/properties.rb
|
107
|
+
- lib/unisec/surrogates.rb
|
108
|
+
- lib/unisec/utils.rb
|
109
|
+
- lib/unisec/version.rb
|
110
|
+
homepage: https://github.com/Acceis/unisec
|
111
|
+
licenses:
|
112
|
+
- MIT
|
113
|
+
metadata:
|
114
|
+
yard.run: yard
|
115
|
+
bug_tracker_uri: https://github.com/Acceis/unisec/issues
|
116
|
+
changelog_uri: https://github.com/Acceis/unisec/releases
|
117
|
+
documentation_uri: https://acceis.github.io/unisec/
|
118
|
+
homepage_uri: https://github.com/Acceis/unisec
|
119
|
+
source_code_uri: https://github.com/Acceis/unisec/
|
120
|
+
rubygems_mfa_required: 'true'
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
require_paths:
|
124
|
+
- lib
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: 3.0.0
|
130
|
+
- - "<"
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '4.0'
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
requirements: []
|
139
|
+
rubygems_version: 3.4.1
|
140
|
+
signing_key:
|
141
|
+
specification_version: 4
|
142
|
+
summary: Unicode Security Toolkit
|
143
|
+
test_files: []
|