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構造を破壊するので注意してください。 - 可能な限り関数は最小限の標準機能にとどめてください。
- 特に
- Previous
- Next