Skip to content
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
61242d6
экспорт схемы в json
kkadmor Jan 17, 2026
af7d70b
кнопка импорта в принтере плат
kkadmor Jan 17, 2026
9f3e797
Рабочий импорт
kkadmor Jan 17, 2026
513e377
добавлены вкладки
kkadmor Jan 25, 2026
c568d19
добавление в бд (не тестил)
kkadmor Jan 25, 2026
64ff86a
Промежуточный
kkadmor Jan 26, 2026
9579f06
промежуточный2
kkadmor Jan 28, 2026
dbe516a
Сохранение с HMAC (без конфига на HMAC ключ)
kkadmor Jan 30, 2026
8dc93e4
Загрузка ключа HMAC через конфиг
kkadmor Jan 30, 2026
cd8fdcb
Убрал все насеры, которые остались после попыток в бд
kkadmor Jan 30, 2026
382acf4
ретурн тру в уи акт
kkadmor Jan 30, 2026
17fa8a2
max_length при 0 не становится бесконечным, убираю
kkadmor Jan 30, 2026
4330cb8
Исправил проверку импорта
kkadmor Jan 30, 2026
c692dd3
Поменял проверку json
kkadmor Jan 30, 2026
1a8b313
микроправки
kkadmor Jan 30, 2026
3f70cbd
еще правки
kkadmor Jan 30, 2026
f47371f
Merge branch 'master220' into import-and-export-integrated-circuitpri…
kkadmor Jan 30, 2026
7bce7a2
фикс тгуи
kkadmor Jan 30, 2026
880f026
фикс для линтера
kkadmor Jan 30, 2026
56e0689
fix
kkadmor Jan 30, 2026
0204376
правки
kkadmor Jan 30, 2026
c2b428d
значение hmac ключа удаляются после использования
kkadmor Jan 30, 2026
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
8 changes: 8 additions & 0 deletions code/__HELPERS/text.dm
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@
for(var/i in 1 to times)
. += string

// Создает строку используя список из ASCII
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

🟡 Рекомендация по стилю: Комментарий в коде должен быть на английском языке. Это требование стайлгайда (пункт 4.1, строка 68) для обеспечения согласованности кодовой базы.

// Creates a string using a list of ASCII codes
References
  1. Комментарии в коде должны быть на английском. (link)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

🟡 Рекомендация по стилю: Согласно стайлгайду (пункт 4.1, правило о комментариях), все комментарии в коде должны быть на английском языке.

// Creates a string using a list of ASCII codes
References
  1. Комментарии должны быть на английском языке и объяснять 'почему', а не 'что'. (link)

/proc/ascii_list2text(list/ascii_text)
. = ""
for(var/ascii_char in ascii_text)
if(ascii_char < 32 || ascii_char > 127)
CRASH("ascii_list2text: invalid ASCII code")
. += ascii2text(ascii_char)

/// Runs sanitize and strip_html_simple
/// I believe strip_html_simple() is required to run first to prevent '<' from displaying as '&lt;' after sanitize() calls byond's html_encode()
/proc/strip_html(t, limit=MAX_MESSAGE_LEN)
Expand Down
2 changes: 2 additions & 0 deletions code/controllers/configuration/entries/config.dm
Original file line number Diff line number Diff line change
Expand Up @@ -856,4 +856,6 @@
/datum/config_entry/flag/smart_cache_assets
default = TRUE

/datum/config_entry/string/hmac_key

/datum/config_entry/flag/generate_assets_in_init
88 changes: 88 additions & 0 deletions code/modules/research/circuitprinter.dm
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,34 @@ using metal and glass, it uses glass and reagents (usually sulfuric acis).

update_static_data_for_all_viewers()

// Проверяет наличие всех данных
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

🟡 Рекомендация по стилю: Комментарий в коде должен быть на английском языке. Это требование стайлгайда (пункт 4.1, строка 68) для обеспечения согласованности кодовой базы.

// Checks for presence of all data
References
  1. Комментарии в коде должны быть на английском. (link)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

🟡 Рекомендация по стилю: Согласно стайлгайду (пункт 4.1, правило о комментариях), все комментарии в коде должны быть на английском языке. В этом файле несколько таких комментариев.

// Checks if all data is present
References
  1. Комментарии должны быть на английском языке и объяснять 'почему', а не 'что'. (link)

/obj/machinery/r_n_d/circuit_imprinter/proc/can_save_circuit_by_json(mob/living/user, json_data)
var/list/data = json_decode(json_data)
var/list/required_keys = list("dupe_data", "name", "materials", "integrated_circuit", "Icon", "IconState", "desc")
for(var/key in required_keys)
if(!LAZYACCESS(data, key))
return FALSE
return TRUE
Comment on lines 313 to 321
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

🟡 Рекомендация: Обнаружена потенциальная ошибка выполнения в процедуре can_save_circuit_by_json. Функция json_decode() может вернуть null, если json_data содержит некорректный JSON. В этом случае последующее обращение к data в цикле for вызовет ошибку. Рекомендую добавить проверку if(!islist(data)) return FALSE после вызова json_decode() для предотвращения падения сервера.

/obj/machinery/r_n_d/circuit_imprinter/proc/can_save_circuit_by_json(mob/living/user, json_data)
	var/list/data = json_decode(json_data)
	if(!islist(data))
		return FALSE
	var/list/required_keys = list("dupe_data", "name", "materials", "integrated_circuit", "Icon", "IconState", "desc")
	for(var/key in required_keys)
		if(!LAZYACCESS(data, key))
			return FALSE
	return TRUE
References
  1. Требовать проверку на null (if(!variable)) перед использованием объекта, особенно после locate(), find_in_view() и т.п. (link)


// Для иморта платы
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

🟡 Рекомендация по стилю: Комментарий в коде должен быть на английском языке. Это требование стайлгайда (пункт 4.1, строка 68) для обеспечения согласованности кодовой базы.

// For board import
References
  1. Комментарии в коде должны быть на английском. (link)

/obj/machinery/r_n_d/circuit_imprinter/proc/save_circuit_by_json(mob/living/user, json_data)
if(!can_save_circuit_by_json(user, json_data))
return

var/list/data = json_decode(json_data)
Comment on lines +324 to +328
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

🟡 Рекомендация по производительности: Процедура save_circuit_by_json вызывает can_save_circuit_by_json, которая уже выполняет json_decode. Затем save_circuit_by_json выполняет json_decode повторно. Это избыточно и немного снижает производительность. Рекомендуется передавать уже декодированные данные из can_save_circuit_by_json или изменить логику, чтобы избежать двойного декодирования.


for(var/list/component_data as anything in scanned_designs)
if(component_data["name"] == data["name"])
balloon_alert(user, "название занято!")
return

LAZYADD(scanned_designs, list(data))

balloon_alert(user, "схема сохранена")
playsound(src, 'sound/machines/ping.ogg', 50)

update_static_data_for_all_viewers()

/obj/machinery/r_n_d/circuit_imprinter/proc/print_module(list/design)
flick("[base_icon_state]_ani", src)

Expand Down Expand Up @@ -376,6 +404,66 @@ using metal and glass, it uses glass and reagents (usually sulfuric acis).
LAZYREMOVE(scanned_designs, design)

update_static_data_for_all_viewers()
if("export")
var/design_id = text2num(params["designId"])

if(design_id < 1 || design_id > length(scanned_designs))
return TRUE

var/list/design = LAZYACCESS(scanned_designs, design_id)

var/mob/user= ui.user

var/json_data = json_encode(design)

var/hmac_key = CONFIG_GET(string/hmac_key) ? CONFIG_GET(string/hmac_key) : ""

// Создает HMAC если ключ корректен
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

🟡 Рекомендация по стилю: Комментарий в коде должен быть на английском языке. Это требование стайлгайда (пункт 4.1, строка 68) для обеспечения согласованности кодовой базы.

			// Creates HMAC if the key is correct
References
  1. Комментарии в коде должны быть на английском. (link)

var/hmac_base64 = ""
if(validate_hmac_key(hmac_key))
hmac_base64 = hmac_md5_base64(hmac_key, json_data)

if(!hmac_base64)
tgui_alert(user, "Ошибка создания электронной подписи!", "Ошибка экспорта")
return TRUE

var/json_base64 = rustg_encode_base64(json_data)
var/final_text = rustg_encode_base64("[json_base64].[hmac_base64]")

// Окно в вводом из которого можно скопировать текст
tgui_input_text(user, "Скопируйте текст схемы:", "Экспорт схемы", default = final_text, max_length=999999999)

if("import")
var/mob/user = ui.user

var/text = tgui_input_text(user, "Вставьте текст интегральной платы", "Импорт схемы", encode=FALSE, max_length = 999999999)
text = text && ascii_list2text(rustlib_decode_base64(text)) ? ascii_list2text(rustlib_decode_base64(text)) : ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

🟡 Рекомендация по улучшению: Вызов ascii_list2text(rustlib_decode_base64(text)) повторяется. Для улучшения читаемости и производительности, результат можно сохранить в переменную.

			var/list/decoded_list = rustlib_decode_base64(text)
			text = text && decoded_list ? ascii_list2text(decoded_list) : ""


var/hmac_key = CONFIG_GET(string/hmac_key) ? CONFIG_GET(string/hmac_key) : ""

if(!text || !findtext(text, "."))
tgui_alert(user, "Ошибка расшифровки. Убедитесь в корректности данных", "Ошибка импорта")
return TRUE

var/list/parts = splittext(text, ".") // [1] - json в виде списка ASCII [2] - HMAC в виде списка ASCII

var/json_data = ascii_list2text(rustlib_decode_base64(parts[1])) ? ascii_list2text(rustlib_decode_base64(parts[1])) : ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

🟡 Рекомендация по улучшению: Аналогично предыдущему замечанию, вызов ascii_list2text(rustlib_decode_base64(parts[1])) повторяется. Рекомендуется сохранить результат в переменную.

			var/list/decoded_json_b64 = rustlib_decode_base64(parts[1])
			var/json_data = decoded_json_b64 ? ascii_list2text(decoded_json_b64) : ""


if(!json_data)
tgui_alert(user, "Некорректный JSON! Проверьте корректность данных.", "Ошибка импорта")
return TRUE

// Создает HMAC если ключ корректен
var/hmac_base64 = ""
if(validate_hmac_key(hmac_key) && json_data)
hmac_base64 = hmac_md5_base64(hmac_key, json_data)

// Сравнивает переданный и полученный HMAC
if(parts[2] != hmac_base64)
tgui_alert(user, "Ошибка электронной подписи! Проверьте корректность данных.", "Ошибка импорта")
return TRUE

save_circuit_by_json(user, json_data)

return TRUE

Expand Down
104 changes: 104 additions & 0 deletions code/modules/wiremod/core/hmac.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#define HMAC_BLOCK_SIZE 64
#define HMAC_IPAD 54 // ASCII "6"
#define HMAC_OPAD 92 // ASCII "\"

/*
* Основная функция HMAC-MD5
* hex_key - ключ в hex-формате
* data - данные для подписи
*/
Comment on lines +5 to +9
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

🟡 Рекомендация по стилю: Согласно стайлгайду (пункт 4.1, правило о комментариях), все комментарии в коде должны быть на английском языке.

/*
 * Main HMAC-MD5 function
 * hex_key - key in hex format
 * data - data to be signed
 */
References
  1. Комментарии должны быть на английском языке и объяснять 'почему', а не 'что'. (link)

/proc/hmac_md5_hex(hex_key, data)
var/key_bin = hex_to_bin(hex_key)

if(!key_bin)
CRASH("hmac_md5_hex: Invalid hex key for HMAC")

if(length(key_bin) > HMAC_BLOCK_SIZE)
key_bin = hex_to_bin(md5(key_bin))

if(length(key_bin) < HMAC_BLOCK_SIZE)
key_bin += repeat_string(HMAC_BLOCK_SIZE - length(key_bin), "0")
Comment on lines +19 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

🔴 Критично: Некорректная реализация HMAC. Ключ дополняется ASCII-символами '0' ("0") вместо нулевых байтов. Согласно RFC 2104, ключ, который короче размера блока, должен быть дополнен до размера блока нулевыми байтами. Текущая реализация использует символ "0", что приводит к неверной генерации подписи и создаёт уязвимость, позволяющую подделку данных платы.

	if(length(key_bin) < HMAC_BLOCK_SIZE)
		key_bin += repeat_string(HMAC_BLOCK_SIZE - length(key_bin), ascii2text(0))


var/ipad = repeat_string(HMAC_BLOCK_SIZE, ascii2text(HMAC_IPAD))
var/opad = repeat_string(HMAC_BLOCK_SIZE, ascii2text(HMAC_OPAD))

var/i_key = xor_strings(key_bin, ipad)
var/o_key = xor_strings(key_bin, opad)

var/inner_hash = md5(i_key + data)

var/inner_hash_bin = hex_to_bin(inner_hash)
var/hmac_hex = md5(o_key + inner_hash_bin)

return hmac_hex

/proc/hmac_md5_base64(hex_key, data)
var/hmac_hex = hmac_md5_hex(hex_key, data)
var/hmac_bin = hex_to_bin(hmac_hex)
return rustg_encode_base64(hmac_bin)

/proc/xor_strings(a, b)
if(length(a) != length(b))
CRASH("XOR: strings of different lengths ([length(a)] vs [length(b)])")

var/result = ""
for(var/i = 1; i <= length(a); i++)
var/byte_a = text2ascii(copytext(a, i, i+1))
var/byte_b = text2ascii(copytext(b, i, i+1))
result += ascii2text(byte_a ^ byte_b)

return result

/proc/hex_to_bin(hex_string)
if(!istext(hex_string))
return null

hex_string = replacetext(hex_string, " ", "")
hex_string = replacetext(hex_string, "\n", "")
hex_string = replacetext(hex_string, "\t", "")

if(length(hex_string) % 2 != 0)
hex_string = "0" + hex_string

var/bin = ""
for(var/i = 1; i <= length(hex_string); i += 2)
var/hex_pair = copytext(hex_string, i, i+2)
var/num = hex2num(hex_pair)

if(isnull(num))
return null

bin += ascii2text(num)

return bin

/proc/bin_to_hex(bin_string)
if(!istext(bin_string))
return ""

var/hex = ""
for(var/i = 1; i <= length(bin_string); i++)
var/byte = text2ascii(copytext(bin_string, i, i+1))
hex += num2hex(byte, 2)

return lowertext(hex)

/proc/validate_hmac_key(hex_key)
if(!istext(hex_key))
return FALSE

var/len = length(hex_key)
if(len != 40 && len != HMAC_BLOCK_SIZE)
return FALSE

var/valid_chars = "0123456789abcdefABCDEF"
for(var/i = 1; i <= len; i++)
var/char = copytext(hex_key, i, i+1)
if(!findtext(valid_chars, char))
return FALSE

return TRUE

#undef HMAC_BLOCK_SIZE
#undef HMAC_IPAD
#undef HMAC_OPAD
5 changes: 5 additions & 0 deletions config/example/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -520,4 +520,9 @@ CACHE_ASSETS 0
## This config option limits the maximum chunk count for which the server will accept a payload, default is 32
TGUI_MAX_CHUNK_COUNT 128

## HMAC key for encode integrated circuit
## Written as hex in a string
## Size - 40 or 64 characters
HMAC_KEY ffffffffffffffffffffffffffffffffffffffff

### INITIALIZATION SETTINGS END ###
1 change: 1 addition & 0 deletions paradise.dme
Original file line number Diff line number Diff line change
Expand Up @@ -3880,6 +3880,7 @@
#include "code\modules\wiremod\core\component.dm"
#include "code\modules\wiremod\core\datatypes.dm"
#include "code\modules\wiremod\core\duplicator.dm"
#include "code\modules\wiremod\core\hmac.dm"
#include "code\modules\wiremod\core\integrated_circuit.dm"
#include "code\modules\wiremod\core\marker.dm"
#include "code\modules\wiremod\core\port.dm"
Expand Down
61 changes: 43 additions & 18 deletions tgui/packages/tgui/interfaces/ComponentPrinter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,22 @@ export const ComponentPrinter = (props) => {
return (
<Window title={'Дубликатор печатных плат'} width={670} height={600}>
<Window.Content>
<Section
title="Сохранённые схемы"
buttons={
<Button icon="file-import" onClick={() => act('import')}>
{toTitleCase('Import')}
</Button>
}
/>
<Box>
{Object.values(designs).length === 0 && (
<Stack.Item mt={1} fontSize={1}>
<NoticeBox info>Сохранённые схемы отсутствуют.</NoticeBox>
</Stack.Item>
)}
{Object.values(designs).map((design) => (
<Section key={design.id}>
<Section key={design.id} style={{ position: 'relative' }}>
<DmIcon
icon={design.icon}
icon_state={design.IconState}
Expand All @@ -62,23 +70,40 @@ export const ComponentPrinter = (props) => {
{toTitleCase(design.name)}
</Button>

{(design.cost &&
Object.keys(design.cost)
.map((mat) => toTitleCase(mat) + ': ' + design.cost[mat])
.join(', ')) || <Box>Ресурсы для печати не требуются.</Box>}

<Button
mr={1}
icon="trash-can"
position="absolute"
right={1}
top={1}
onClick={() =>
act('del_design', {
designId: design.id,
})
}
/>
<Box style={{ display: 'inline' }}>
{(design.cost &&
Object.keys(design.cost)
.map((mat) => toTitleCase(mat) + ': ' + design.cost[mat])
.join(', ')) || <Box>Ресурсы для печати не требуются.</Box>}
</Box>
<Box
style={{
position: 'absolute',
right: '8px',
top: '8px',
display: 'flex',
gap: '5px',
}}
>
<Button
icon="save"
onClick={() =>
act('export', {
designId: design.id,
})
}
>
{toTitleCase('Export')}
</Button>
<Button
icon="trash-can"
onClick={() =>
act('del_design', {
designId: design.id,
})
}
/>
</Box>
</Section>
))}
</Box>
Expand Down
2 changes: 1 addition & 1 deletion tgui/public/tgui.bundle.js

Large diffs are not rendered by default.

Loading