Skip to content
Kward Search API index

Class: Kward::Skills::TrustStore

Inherits:
Object
  • Object
show all
Defined in:
lib/kward/skills/trust_store.rb

Overview

Persists workspace-scoped decisions for project skill snapshots.

Constant Summary collapse

DECISIONS =
%w[allow deny].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config_dir:) ⇒ TrustStore

Returns a new instance of TrustStore.



16
17
18
# File 'lib/kward/skills/trust_store.rb', line 16

def initialize(config_dir:)
  @path = File.join(File.expand_path(config_dir), "trusted_project_skills.json")
end

Instance Attribute Details

#pathObject (readonly)

Returns the value of attribute path.



14
15
16
# File 'lib/kward/skills/trust_store.rb', line 14

def path
  @path
end

Class Method Details

.digest_files(paths, root: nil) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/kward/skills/trust_store.rb', line 59

def self.digest_files(paths, root: nil)
  root = File.realpath(root) if root
  digest = Digest::SHA256.new
  paths.map { |path| File.realpath(path) }.sort.each do |path|
    raise ArgumentError, "file is outside digest root" if root && !PathGuard.inside?(path, root)

    relative_path = root ? Pathname.new(path).relative_path_from(Pathname.new(root)).to_s : path
    digest.update(relative_path)
    digest.update("\0")
    digest.update(File.binread(path))
    digest.update("\0")
  end
  digest.hexdigest
end

Instance Method Details

#decision(workspace_root:, skill_path:, digest:) ⇒ Object



20
21
22
23
24
25
26
27
# File 'lib/kward/skills/trust_store.rb', line 20

def decision(workspace_root:, skill_path:, digest:)
  record = skill_record(workspace_root: workspace_root, skill_path: skill_path)
  return unless record
  return unless record["digest"] == digest.to_s

  decision = record["decision"]
  decision if DECISIONS.include?(decision)
end

#remove_skill!(workspace_root:, skill_path:) ⇒ Object



52
53
54
55
56
57
# File 'lib/kward/skills/trust_store.rb', line 52

def remove_skill!(workspace_root:, skill_path:)
  config = read_config
  workspace = config.fetch("workspaces", {})[workspace_key(workspace_root)]
  workspace&.fetch("skills", {})&.delete(skill_key(workspace_root, skill_path))
  PrivateFile.write_json(path, config)
end

#remove_workspace!(workspace_root:) ⇒ Object



46
47
48
49
50
# File 'lib/kward/skills/trust_store.rb', line 46

def remove_workspace!(workspace_root:)
  config = read_config
  config.fetch("workspaces", {}).delete(workspace_key(workspace_root))
  PrivateFile.write_json(path, config)
end

#set_decision!(workspace_root:, skill_path:, digest:, decision:) ⇒ Object

Raises:

  • (ArgumentError)


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/kward/skills/trust_store.rb', line 29

def set_decision!(workspace_root:, skill_path:, digest:, decision:)
  decision = decision.to_s
  raise ArgumentError, "invalid project skill trust decision: #{decision}" unless DECISIONS.include?(decision)

  config = read_config
  workspace = workspace_key(workspace_root)
  skill = skill_key(workspace_root, skill_path)
  config["workspaces"] ||= {}
  config["workspaces"][workspace] ||= { "skills" => {} }
  config["workspaces"][workspace]["skills"] ||= {}
  config["workspaces"][workspace]["skills"][skill] = {
    "digest" => digest.to_s,
    "decision" => decision
  }
  PrivateFile.write_json(path, config)
end