55 lines
1.4 KiB
Ruby
55 lines
1.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Hledger
|
|
# Extracts the single fenced `hledger` journal from a post's raw markdown and
|
|
# rejects input that could make hledger read files or resources we do not
|
|
# control.
|
|
class Journal
|
|
FENCE = /^[ \t]*`{3,}hledger[ \t]*\r?\n(?<source>.*?)^[ \t]*`{3,}[ \t]*$/m
|
|
INCLUDE = /^[ \t]*!?include\b/i
|
|
MAX_LINES = 5_000
|
|
|
|
Result =
|
|
Struct.new(:source, :error, keyword_init: true) do
|
|
def ok?
|
|
error.nil?
|
|
end
|
|
|
|
def none?
|
|
error == :none
|
|
end
|
|
end
|
|
|
|
def self.extract(raw)
|
|
new(raw).extract
|
|
end
|
|
|
|
def initialize(raw)
|
|
@raw = raw.to_s
|
|
end
|
|
|
|
def extract
|
|
matches = @raw.scan(FENCE).flatten
|
|
return Result.new(source: nil, error: :none) if matches.empty?
|
|
return Result.new(source: nil, error: :ambiguous) if matches.length > 1
|
|
|
|
source = matches.first
|
|
validation_error = validate(source)
|
|
return Result.new(source: nil, error: validation_error) if validation_error
|
|
|
|
Result.new(source: source, error: nil)
|
|
end
|
|
|
|
private
|
|
|
|
def validate(source)
|
|
return :too_large if source.bytesize > SiteSetting.hledger_max_journal_bytes
|
|
return :too_many_lines if source.lines.size > MAX_LINES
|
|
return :invalid_encoding unless source.valid_encoding?
|
|
return :include_not_allowed if source.match?(INCLUDE)
|
|
|
|
nil
|
|
end
|
|
end
|
|
end
|