BoardGuard DSL
1. 概要
BoardGuard DSL(以下 DSL)は、Perl 準拠の構文で、条件付き規制を行えるツールです。各ルールは個別のサブルーチンとして定義されます。
2. ファイル構造
DSL ファイル全体は、以下の要素から構成されます。
- トップレベルコード(オプション)
my/ourなどの変数定義を自由に書けます。- ただし Safe コンパートメント上で動作するため、許可されていない関数呼び出し(
system,openなど)は実行時に制限されます。
- ルール定義(必須)
- ルールごとに以下のような形式で記述します:
Rule<RuleName> sub { # $ctx 参照で入力値を読み取り。$outは変更の反映用 my ($ctx, $out) = @_; # 処理内容 return _DENY_; # または _ACCEPT_。省略された場合次のルールの評価に移る( _PASS_ )。 } - 複数ルールを順次定義できます。
- ルール名 (
RuleName) は英数字とアンダースコアのみを含む単語(先頭に数字不可)とします。
- ルールごとに以下のような形式で記述します:
3. トップレベルコード
3.1 ヘルパー関数の定義
- ファイル冒頭(ルール定義の前)に、共通で利用するヘルパー関数 (
sub HelperFoo { … }) を記述できます。 - 例:
# DSL ヘルパー例 sub HelperIsAdmin { my ($ctx) = @_; return ($ctx->{user_info}{is_admin} // 0) == 1 ? 1 : 0; } - Safe コンパートメント上で一度だけ評価されます。複数ルール間で共通利用できます。
3.2 変数定義(グローバル or my/our)
- トップレベルで
myやourを使って変数を定義しても問題ありません。ただし Safe 上で実行されるため、外部への影響はありません。my $count = 0; our $VERSION = '1.0'; - 定義された変数は、同一ファイル内のすべてのルール定義で参照可能です。
4. ルール定義の文法
4.1 基本形
各ルールは必ず次の形で書きます。ルール名は Rule プレフィックス+英数字文字列とします。
Rule<RuleName> sub {
my ($ctx, $out) = @_;
# ルールの処理内容を書く
# 最終的に _DENY_ か _ACCEPT_ を return する。省略された場合または _PASS_ で次のルールに移行。
if ( 条件 ) {
# 何らかの出力設定は %out に書き込む
$out->{error_code} = $E_FORM_LONGSUBJECT;
return _DENY_;
}
# 条件を満たさなければ許可
return _ACCEPT_;
}
Rule<RuleName><RuleName>部分はRuleに続く英数字・アンダースコアのみとします。例:RuleSpamDetect,RuleAdminOnlyなど。- 先頭文字はアルファベットで始めてください。
sub { … }- ルール本文は無名サブルーチン(
sub { ... })で囲みます。 - 引数は必ず
(my ($ctx) = @_)の形で、外部から渡された%ctxを読み取ります。
- ルール本文は無名サブルーチン(
4.2 %ctx と %out の扱い
-
読み取り専用の
%ctx
DSL 実行時に、外部(アプリケーション側)から以下のキーを含むハッシュリファレンス%ctxが渡されます。キー名 内容 message投稿本文 mailメール欄またはコマンド欄 name名前欄 titleスレ立て時のタイトル time投稿時刻(タイムスタンプ) thread_idスレッド ID(スレ投稿時) thread_titleスレッドタイトル(2レス目以降) bbs掲示板ディレクトリ名 fpフィンガープリント ip投稿者 IP アドレス hostホスト名 uaユーザーエージェント session_idセッション ID(忍法帖 ID) cap_idキャップ ID setting掲示板設定情報のハッシュリファレンス attrスレッド属性情報のハッシュリファレンス user_infoユーザ情報のハッシュリファレンス(忍法帖情報など) scoreスコア管理用数値 unique独自拡張用のハッシュリファレンス(空または追加値) - ルール内で
$ctx->{キー名}を読み取り、判定ロジックに利用します。 $ctxを書き換えても呼び出し元に影響はありません。
- ルール内で
-
書き込み先の
%out
ルール内で結果をアプリケーション側に返すときは、%outに必要なキーを書き込みます。キー名 内容 error_codeエラー時に返す整数コード( $ZP::…定数など)error_messageエラーメッセージの文字列 error_subjectエラー件名(コマンドエラー時に出す件名) message投稿本文の書き換え結果 mailメール欄の書き換え結果 name名前欄の書き換え結果 titleスレタイの書き換え結果(スレ立て時のみ) thread_updownフロート制御(top/bottom/down/age/sage/[±数値]) attrスレッド属性ハッシュリファレンス user_infoユーザ情報ハッシュリファレンス unique独自拡張用ハッシュリファレンス 例:
$out->{error_code} = 100000; $out->{error_subject} = "長文規制!"; $out->{error_message} = "長文を書くには忍法帖レベルが足りません。"; return _DENY_; -
エラーコードについて
error_codeにはmodule/constant.plにて定義されている各定数が使えます。
オリジナルのエラー内容にする場合は、error_codeに既存のエラーコード以外の値を入れた上で、error_subject、error_messageにエラーメッセージをセットしてください。
4.3 戻り値(DENY / ACCEPT / PASS)
_DENY_:定数0を返し、当該ルールで拒否判定を行う。以降のルール評価はスキップされる。_ACCEPT_:定数1を返し、書き込みを許可する。以降のルール評価はスキップされる。_PASS_:定数2を返し、次のルールの評価に移る。
すべてのルールがreturnに到達しなかった場合、DSL 全体の戻り値は 1(許可)となる。
注意:
- いずれかのルールが
_DENY_または_ACCEPT_を返すと、それ以降のルールは評価されず、即座に結果が返される。 - ルール内で例外・エラー(タイムアウトや未定義メソッド呼び出しなど)が発生した場合、当該ルールだけをスキップし、次のルールへ進みます。
5. ルール名および重複チェック
- ルール名の命名規則
Ruleプレフィックスに続く名前部分は、先頭にアルファベット、続いて英数字・アンダースコアのみを使えます。- 例:
RuleSpamDetect、RuleLengthCheck、RuleAdminOnly
- 例:
- ルール名には空白、記号(アンダースコア以外)、全角文字は禁止です。
- 重複ルール名の扱い
- 同じファイル内で同一のルール名を複数定義すると、構文チェックモードではステータス
4(重複ルール名エラー)となり、DSL 評価時でも最初のエラー検出状態となることがあります。 - 重複ルール名を避けるよう記述してください。
- 同じファイル内で同一のルール名を複数定義すると、構文チェックモードではステータス
6. コメント
- Perl と同様に、以下の形式でコメントを書けます。
- 行末コメント:
#以降は無視RuleFoo sub { my ($ctx) = @_; # 引数の受け取り … } - 複数行コメント(ヒアドキュメント状にはできない。単純に各行に
#を付与)# ここは複数行コメント # すべて '#' を先頭に置く
- 行末コメント:
7. 正規表現の扱い
- ルール内で Perl の正規表現(
m//,qr//,s///,/…/)が使えます。 - ただし正規表現に文法エラー(未閉鎖スラッシュ、エスケープ不足など)があると Safe 上の評価でエラーとなり、正規表現文法エラーとして扱われます。
例:
RuleSpamDetect sub {
my ($ctx) = @_;
# 禁止ワードのリストを正規表現でチェック
if ($ctx->{message} =~ /spam\d+/) {
return _DENY_;
}
return _ACCEPT_;
}
8. サンプル DSL ファイル例
#------------------------------------------------------------------------------#
# Helper 定義(任意)
#------------------------------------------------------------------------------#
# 忍法帖データのユーザー説明欄`user_desc`に「荒らし」というワードが入っていた場合真となる関数
sub HelperIsArashi {
my ($ctx) = @_;
return (($ctx->{user_info}{user_desc} =~ /荒らし/) ? 1 : 0;
}
#------------------------------------------------------------------------------#
# ルール定義
#------------------------------------------------------------------------------#
# ルール1: 禁止ワードチェック
RuleSpamDetect sub {
my ($ctx, $out) = @_;
# 「spam123」, 「badword」, 「暴言」のいずれかが含まれていれば拒否
my @blacklist = qw(spam123 badword 暴言);
foreach my $w (@blacklist) {
if ($ctx->{message} =~ /\Q$w\E/i) {
return _DENY_;
}
}
}
# ルール2: タイトル文字数チェック(スレ立て時のみ)
RuleTitleLength sub {
my ($ctx, $out) = @_;
if (defined $ctx->{title} && length($ctx->{title}) > 50) {
$out->{error_code} = 100001;
$out->{error_subject} = "スレタイなげーぞ";
$out->{error_message} = "端的に書けや";
return _DENY_;
}
}
# ルール3: 荒らしに適当なエラーメッセージを見せる
RuleNoArashi sub {
my ($ctx, $out) = @_;
if ( HelperIsArashi($ctx) ) {
srand($ctx->{time});
$out->{error_code} = 600+int(rand(5));
return _DENY_;
}
}
#------------------------------------------------------------------------------#
# 任意のトップレベルコード
#------------------------------------------------------------------------------#
# 変数 $counter を共通で使いたい場合など
my $counter = 0;
9. 注意事項
- Safe コンパートメントの制限
- DSL の中では
system,open,forkなどの危険な関数は呼べません。 - 安全上、ファイルI/O や外部コマンド実行が必要な場合は直接実装してください。
- DSL の中では
- トップレベルで Perl モジュールを
useするとエラーになることがある- Safe 上で実行されるため、トップレベルで
use Some::Module;のようにモジュールをロードしても、Safe の許可リストにない機能を呼ぼうとするとエラーになります。 - 可能な限りヘルパー関数は最小限の標準機能にとどめてください。
- Safe 上で実行されるため、トップレベルで
- DSL側で任意の変更を行って$outにより反映させる場合
- 特に
massage,name,mail,titleについて、<,>等htmlとして解釈される文字を通常のテキストとして使う場合は>,<など適切にエスケープしてください。特に<>はdatの区切り文字であるため、原則使用するべきではありません。また改行コード\nもdat構造を破壊するので注意してください。 - 可能な限り関数は最小限の標準機能にとどめてください。
- 特に