Added some initial work on checking safe files for malicious contents
This commit is contained in:
parent
3080806d8c
commit
266b5b7e4f
@ -2,3 +2,4 @@ class_name SaveData
|
|||||||
extends Resource
|
extends Resource
|
||||||
|
|
||||||
@export_file("*.tscn", "*.scn") var current_world: String
|
@export_file("*.tscn", "*.scn") var current_world: String
|
||||||
|
@export var com_hash: int = -1 # computer hash for checking, if this came from another source.
|
||||||
|
|||||||
@ -103,6 +103,8 @@ func update_world_state() -> void:
|
|||||||
|
|
||||||
world_state.loaded_level_ids = get_loaded_level_ids()
|
world_state.loaded_level_ids = get_loaded_level_ids()
|
||||||
|
|
||||||
|
world_state.com_hash = Game.get_computer_hash()
|
||||||
|
|
||||||
|
|
||||||
func get_level_ids_from_streamers(streamers: Array[LevelStreamer]) -> Array[StringName]:
|
func get_level_ids_from_streamers(streamers: Array[LevelStreamer]) -> Array[StringName]:
|
||||||
var streamer_ids: Array[StringName] = []
|
var streamer_ids: Array[StringName] = []
|
||||||
|
|||||||
@ -5,3 +5,4 @@ extends Resource
|
|||||||
@export_file("*.tscn", "*.scn") var world_path: String
|
@export_file("*.tscn", "*.scn") var world_path: String
|
||||||
@export var loaded_level_ids: Array[StringName] = []
|
@export var loaded_level_ids: Array[StringName] = []
|
||||||
@export var instance_data: Dictionary[String, Variant] = {}
|
@export var instance_data: Dictionary[String, Variant] = {}
|
||||||
|
@export var com_hash: int = -1 # computer hash for checking, if this came from another source.
|
||||||
|
|||||||
@ -22,7 +22,7 @@ var main_menu: MainMenu
|
|||||||
|
|
||||||
var _os_dialog_window_result: int = -1
|
var _os_dialog_window_result: int = -1
|
||||||
|
|
||||||
@onready var world_holder: Node = %WorldHolder
|
@onready var scene_holder: Node = %SceneHolder
|
||||||
@onready var loading_screen: LoadingScreen = %LoadingScreen
|
@onready var loading_screen: LoadingScreen = %LoadingScreen
|
||||||
|
|
||||||
|
|
||||||
@ -34,6 +34,19 @@ static func debug_draw(draw_shape: String, args: Array) -> void:
|
|||||||
const DD3D: StringName = &"DebugDraw3D"
|
const DD3D: StringName = &"DebugDraw3D"
|
||||||
if is_debug and Engine.has_singleton(DD3D):
|
if is_debug and Engine.has_singleton(DD3D):
|
||||||
Engine.get_singleton(DD3D).callv("draw_" + draw_shape, args)
|
Engine.get_singleton(DD3D).callv("draw_" + draw_shape, args)
|
||||||
|
|
||||||
|
|
||||||
|
static func get_computer_hash() -> int:
|
||||||
|
var combined_str: String = str(
|
||||||
|
OS.get_processor_name(),
|
||||||
|
OS.get_processor_count(),
|
||||||
|
OS.get_model_name(),
|
||||||
|
OS.get_name(),
|
||||||
|
)
|
||||||
|
|
||||||
|
return combined_str.hash()
|
||||||
|
|
||||||
|
|
||||||
## Turns res://example.tscn into example.tres (and .scn -> .res) and uid://1234567 into 1234567.tres.
|
## Turns res://example.tscn into example.tres (and .scn -> .res) and uid://1234567 into 1234567.tres.
|
||||||
static func convert_path_to_resource_filename(path: String) -> String:
|
static func convert_path_to_resource_filename(path: String) -> String:
|
||||||
path = ResourceUID.path_to_uid(path)
|
path = ResourceUID.path_to_uid(path)
|
||||||
@ -50,12 +63,37 @@ func _ready() -> void:
|
|||||||
register_commands()
|
register_commands()
|
||||||
|
|
||||||
|
|
||||||
|
func check_computer_hash(input_hash: int) -> bool:
|
||||||
|
if input_hash == Game.get_computer_hash():
|
||||||
|
return true
|
||||||
|
|
||||||
|
_os_dialog_window_result = -1
|
||||||
|
|
||||||
|
var _on_dialog_interacted: Callable = func(button_id: int) -> void:
|
||||||
|
_os_dialog_window_result = button_id
|
||||||
|
|
||||||
|
DisplayServer.dialog_show(
|
||||||
|
"Save Hash Mismatch",
|
||||||
|
"The save you are trying to load seems to come from a different computer.\nWe are not responsible for any damages to your computer if the save is malicious.\n\nDo you want to load the save anyway?",
|
||||||
|
["Abort", "Continue"], _on_dialog_interacted)
|
||||||
|
|
||||||
|
get_tree().root.process_mode = Node.PROCESS_MODE_DISABLED
|
||||||
|
|
||||||
|
while _os_dialog_window_result == -1:
|
||||||
|
await get_tree().process_frame
|
||||||
|
|
||||||
|
get_tree().root.process_mode = Node.PROCESS_MODE_PAUSABLE
|
||||||
|
|
||||||
|
return _os_dialog_window_result != 0
|
||||||
|
|
||||||
|
|
||||||
func load_game(_save_data: SaveData, _save_slot: int) -> void:
|
func load_game(_save_data: SaveData, _save_slot: int) -> void:
|
||||||
if not await check_computer_hash(save_data.com_hash):
|
if not await check_computer_hash(_save_data.com_hash):
|
||||||
return
|
return
|
||||||
|
|
||||||
save_data = _save_data.duplicate()
|
save_data = _save_data.duplicate()
|
||||||
save_slot = _save_slot
|
save_slot = _save_slot
|
||||||
|
|
||||||
InputManager.capture_mouse()
|
InputManager.capture_mouse()
|
||||||
load_world(save_data.current_world, false)
|
load_world(save_data.current_world, false)
|
||||||
|
|
||||||
@ -69,6 +107,8 @@ func save_game() -> Error:
|
|||||||
if is_instance_valid(world):
|
if is_instance_valid(world):
|
||||||
save_data.current_world = ResourceUID.path_to_uid(world.scene_file_path)
|
save_data.current_world = ResourceUID.path_to_uid(world.scene_file_path)
|
||||||
|
|
||||||
|
save_data.com_hash = Game.get_computer_hash()
|
||||||
|
|
||||||
DirAccess.make_dir_recursive_absolute(SAVE_DIR % save_slot)
|
DirAccess.make_dir_recursive_absolute(SAVE_DIR % save_slot)
|
||||||
return ResourceSaver.save(save_data, SAVE_PATH % [save_slot, file_name])
|
return ResourceSaver.save(save_data, SAVE_PATH % [save_slot, file_name])
|
||||||
|
|
||||||
@ -97,7 +137,7 @@ func load_world(world_path: String, save_previous: bool = true, load_from_save:
|
|||||||
world.change_world_requested.connect(load_world)
|
world.change_world_requested.connect(load_world)
|
||||||
world.world_unload_requested.connect(_on_unload_world_request)
|
world.world_unload_requested.connect(_on_unload_world_request)
|
||||||
|
|
||||||
world_holder.add_child.call_deferred(world)
|
scene_holder.add_child.call_deferred(world)
|
||||||
await world.loaded
|
await world.loaded
|
||||||
loading_screen.fade_out()
|
loading_screen.fade_out()
|
||||||
return OK
|
return OK
|
||||||
@ -144,7 +184,7 @@ func load_main_menu() -> void:
|
|||||||
_main_menu.load_game_request.connect(load_game)
|
_main_menu.load_game_request.connect(load_game)
|
||||||
_main_menu.quit_request.connect(quit_game)
|
_main_menu.quit_request.connect(quit_game)
|
||||||
main_menu = _main_menu
|
main_menu = _main_menu
|
||||||
world_holder.add_child(main_menu)
|
scene_holder.add_child(main_menu)
|
||||||
|
|
||||||
|
|
||||||
func load_startup() -> void:
|
func load_startup() -> void:
|
||||||
@ -155,7 +195,7 @@ func load_startup() -> void:
|
|||||||
load_main_menu()
|
load_main_menu()
|
||||||
loading_screen.fade_out()
|
loading_screen.fade_out()
|
||||||
)
|
)
|
||||||
add_child(startup)
|
scene_holder.add_child(startup)
|
||||||
|
|
||||||
|
|
||||||
func unload_main_menu() -> void:
|
func unload_main_menu() -> void:
|
||||||
@ -170,12 +210,14 @@ func quit_game() -> void:
|
|||||||
|
|
||||||
func register_commands() -> void:
|
func register_commands() -> void:
|
||||||
var load_save_func: Callable = func(save_index: int) -> void:
|
var load_save_func: Callable = func(save_index: int) -> void:
|
||||||
|
print("Loading Save index ", save_index)
|
||||||
var path: String = MainMenu.get_save_path(save_index)
|
var path: String = MainMenu.get_save_path(save_index)
|
||||||
if ResourceLoader.exists(path):
|
if ResourceLoader.exists(path):
|
||||||
load_game(load(path), save_index)
|
load_game(load(path), save_index)
|
||||||
|
|
||||||
LimboConsole.register_command(load_save_func, "load_save", "Loads the save with the given save slot index.")
|
LimboConsole.register_command(load_save_func, "load_save", "Loads the save with the given save slot index.")
|
||||||
LimboConsole.register_command(save_game, "save_game", "Saves the game based on the current save slot.")
|
LimboConsole.register_command(save_game, "save_game", "Saves the game based on the current save slot.")
|
||||||
|
#DebugConsole.register_command("load_save", load_save_func, "Loads the save with the given save slot index.")
|
||||||
|
|
||||||
|
|
||||||
func _on_unload_world_request(do_save: bool) -> void:
|
func _on_unload_world_request(do_save: bool) -> void:
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
script = ExtResource("1_7uq6d")
|
script = ExtResource("1_7uq6d")
|
||||||
metadata/_custom_type_script = "uid://cl1u038dbrou2"
|
metadata/_custom_type_script = "uid://cl1u038dbrou2"
|
||||||
|
|
||||||
[node name="WorldHolder" type="Node" parent="." unique_id=1835125942]
|
[node name="SceneHolder" type="Node" parent="." unique_id=1835125942]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
||||||
[node name="CanvasLayer" type="CanvasLayer" parent="." unique_id=919699501]
|
[node name="CanvasLayer" type="CanvasLayer" parent="." unique_id=919699501]
|
||||||
|
|||||||
@ -49,6 +49,16 @@ static func get_save_path(slot_index: int) -> String:
|
|||||||
return SAVE_DATA_PATH % slot_index
|
return SAVE_DATA_PATH % slot_index
|
||||||
|
|
||||||
|
|
||||||
|
static func does_save_contents_contain_code(save_path: String) -> bool:
|
||||||
|
var file := FileAccess.open(save_path, FileAccess.READ)
|
||||||
|
var contents: String = file.get_as_text()
|
||||||
|
if contents.containsn("func") or contents.containsn("script/source") or contents.containsn("_init"):
|
||||||
|
OS.alert("The save you are trying to load contains malicious code.\nAborting...", "Save File Warning")
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
continue_button.pressed.connect(_on_continue_pressed)
|
continue_button.pressed.connect(_on_continue_pressed)
|
||||||
load_button.pressed.connect(_on_load_pressed)
|
load_button.pressed.connect(_on_load_pressed)
|
||||||
@ -98,6 +108,9 @@ func _on_continue_pressed() -> void:
|
|||||||
_on_new_game_pressed()
|
_on_new_game_pressed()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if does_save_contents_contain_code(path):
|
||||||
|
return
|
||||||
|
|
||||||
config.set_value("save", "last_slot", slot)
|
config.set_value("save", "last_slot", slot)
|
||||||
config.save(Game.USER_SETTINGS_PATH)
|
config.save(Game.USER_SETTINGS_PATH)
|
||||||
|
|
||||||
@ -112,6 +125,8 @@ func _on_load_pressed() -> void:
|
|||||||
|
|
||||||
func _on_new_game_pressed() -> void:
|
func _on_new_game_pressed() -> void:
|
||||||
var save_data: SaveData = Game.INITIAL_SAVE_DATA.duplicate()
|
var save_data: SaveData = Game.INITIAL_SAVE_DATA.duplicate()
|
||||||
|
save_data.com_hash = Game.get_computer_hash()
|
||||||
|
print("Save hash: ", save_data.com_hash, " Computer hash: ", Game.get_computer_hash())
|
||||||
var slot_index: int = get_unique_save_slot_index()
|
var slot_index: int = get_unique_save_slot_index()
|
||||||
load_game_request.emit(save_data, slot_index)
|
load_game_request.emit(save_data, slot_index)
|
||||||
|
|
||||||
@ -139,6 +154,15 @@ func _on_save_entry_pressed(save_slot: int) -> void:
|
|||||||
_on_new_game_pressed()
|
_on_new_game_pressed()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# TODO: Check for hash before loading the file via 'load'
|
||||||
|
if does_save_contents_contain_code(save_path):
|
||||||
|
return
|
||||||
|
#var file := FileAccess.open(save_path, FileAccess.READ)
|
||||||
|
#var contents: String = file.get_as_text()
|
||||||
|
#var index: int = contents.find("com_hash = ")
|
||||||
|
#var hash_str: String = contents.get_slice("\n", 1)
|
||||||
|
#print("Hash: ", hash_str, " Index: ", index, "\nContents: ", contents)
|
||||||
|
|
||||||
var save_data: SaveData = load(save_path)
|
var save_data: SaveData = load(save_path)
|
||||||
load_game_request.emit(save_data, save_slot)
|
load_game_request.emit(save_data, save_slot)
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user