@o-lang/olang 1.2.35 → 1.2.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/parser/index.js +16 -0
- package/src/runtime/RuntimeAPI.js +174 -116
package/package.json
CHANGED
package/src/parser/index.js
CHANGED
|
@@ -462,6 +462,22 @@ if (returnMatch) {
|
|
|
462
462
|
|
|
463
463
|
flushCurrentStep(); // ✅ Final flush
|
|
464
464
|
|
|
465
|
+
// ✅ FALLBACK: Scan raw lines for Return if regex missed it (Windows line endings, hidden chars, etc.)
|
|
466
|
+
if (workflow.returnValues.length === 0) {
|
|
467
|
+
for (let j = 0; j < lines.length; j++) {
|
|
468
|
+
const clean = lines[j].replace(/\r/g, '').trim();
|
|
469
|
+
const match = clean.match(/^Return\s+(.+)$/i);
|
|
470
|
+
if (match) {
|
|
471
|
+
console.log(`[PARSER] Recovered Return at line ${j+1}: "${clean}"`);
|
|
472
|
+
workflow.returnValues = match[1]
|
|
473
|
+
.split(',')
|
|
474
|
+
.map(r => r.trim())
|
|
475
|
+
.filter(r => r !== '');
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
465
481
|
// Post-process Save as in actionRaw - ✅ Apply normalization
|
|
466
482
|
workflow.steps.forEach(step => {
|
|
467
483
|
if (step.actionRaw && step.saveAs === null) {
|
|
@@ -771,128 +771,186 @@ class RuntimeAPI {
|
|
|
771
771
|
// -----------------------------
|
|
772
772
|
// ✅ KERNEL-LEVEL INPUT VALIDATION (Pre-Flight Safety)
|
|
773
773
|
// -----------------------------
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
774
|
+
// -----------------------------
|
|
775
|
+
// ✅ KERNEL-LEVEL INPUT VALIDATION (Pre-Flight Safety) - ENHANCED WITH CONTEXTUAL ALLOWLIST
|
|
776
|
+
// -----------------------------
|
|
777
|
+
_validateInputs(inputs) {
|
|
778
|
+
// Only scan specific input fields that contain user text
|
|
779
|
+
const fieldsToScan = ['user_message', 'user_question', 'text', 'prompt', 'document_text'];
|
|
780
|
+
|
|
781
|
+
for (const field of fieldsToScan) {
|
|
782
|
+
const text = inputs[field];
|
|
783
|
+
if (!text || typeof text !== 'string') continue;
|
|
784
|
+
|
|
785
|
+
// 🔒 CONJUGATION-AWARE + EVASION-RESISTANT PAN-AFRICAN INTENT DETECTION (INPUT)
|
|
786
|
+
const forbiddenPatterns = [
|
|
787
|
+
// ────────────────────────────────────────────────
|
|
788
|
+
// 🇳🇬 NIGERIAN LANGUAGES (Fixed Unicode Boundaries)
|
|
789
|
+
// ────────────────────────────────────────────────
|
|
790
|
+
|
|
791
|
+
// YORUBA: Removed trailing \b after 'ṣẹ' to fix Unicode matching
|
|
792
|
+
{ pattern: /fi\s+(?:owo|ẹ̀wọ̀|ewo|ku|fun|s'ọkọọ)/i, capability: 'transfer', lang: 'yo' },
|
|
793
|
+
{ pattern: /ranṣẹ\s+(?:owo|pesa|kuɗi|ego)/i, capability: 'transfer', lang: 'yo' },
|
|
794
|
+
{ pattern: /fi\s+\w+\s+\w+\s+ranṣẹ/i, capability: 'transfer', lang: 'yo' }, // Catches "Fi 5000 naira ranṣẹ"
|
|
795
|
+
{ pattern: /san\s+(?:owo|ẹ̀wọ̀|ewo|fun|wo)/i, capability: 'payment', lang: 'yo' },
|
|
796
|
+
{ pattern: /gba\s+owo/i, capability: 'withdrawal', lang: 'yo' },
|
|
797
|
+
{ pattern: /\bti\s+(?:fi|san|gba|da|lo)/i, capability: 'unauthorized_action', lang: 'yo' },
|
|
798
|
+
{ pattern: /\b(?:ń|ǹ|n)\s+(?:fi|san|gba)/i, capability: 'unauthorized_action', lang: 'yo' },
|
|
799
|
+
{ pattern: /\b(mo\s+ti\s+(?:fi|san|gba))/i, capability: 'unauthorized_action', lang: 'yo' },
|
|
800
|
+
|
|
801
|
+
// HAUSA: ✅ FIXED - Aggressive Substring Match (No Boundaries)
|
|
802
|
+
{ pattern: /aika.{0,30}ku(?:ɗ|d)i/iu, capability: 'transfer', lang: 'ha' },
|
|
803
|
+
{ pattern: /ciyar\s*(?:da)?/i, capability: 'transfer', lang: 'ha' },
|
|
804
|
+
{ pattern: /shiga\s+ku(?:ɗ|d)i/iu, capability: 'transfer', lang: 'ha' },
|
|
805
|
+
{ pattern: /turo\s+.*\s+aika/i, capability: 'transfer', lang: 'ha' },
|
|
806
|
+
{ pattern: /biya\s*(?:da)?/i, capability: 'payment', lang: 'ha' },
|
|
807
|
+
{ pattern: /sahaw[ae]\s+ku(?:ɗ|d)i/iu, capability: 'withdrawal', lang: 'ha' },
|
|
808
|
+
{ pattern: /(?:ya|ta|su)\s+(?:ciyar|biya|sahawa|sake)/i, capability: 'unauthorized_action', lang: 'ha' },
|
|
809
|
+
{ pattern: /(?:za\s+a|za\s+ta)\s+(?:ciyar|biya)/i, capability: 'unauthorized_action', lang: 'ha' },
|
|
810
|
+
{ pattern: /ina\s+(?:ciyar|biya|sahawa)/i, capability: 'unauthorized_action', lang: 'ha' },
|
|
811
|
+
|
|
812
|
+
|
|
813
|
+
// IGBO: Removed trailing \b after 'igo'
|
|
814
|
+
{ pattern: /zipu\s+(?:ego|moni|isi|na)/i, capability: 'transfer', lang: 'ig' },
|
|
815
|
+
{ pattern: /buru\s+(?:ego|moni|isi)/i, capability: 'transfer', lang: 'ig' },
|
|
816
|
+
{ pattern: /zi\s+.*\s+zipu/i, capability: 'transfer', lang: 'ig' },
|
|
817
|
+
{ pattern: /tinye\s+(?:ego|moni|isi)/i, capability: 'deposit', lang: 'ig' },
|
|
818
|
+
{ pattern: /(?:ziri|bururu|tinyere|gbara)/i, capability: 'unauthorized_action', lang: 'ig' },
|
|
819
|
+
{ pattern: /m\s+(?:ziri|buru|zipuru|tinyere)/i, capability: 'unauthorized_action', lang: 'ig' },
|
|
820
|
+
|
|
821
|
+
// SWAHILI: ✅ FIXED - Catch Conjugated Forms (ni-li-pe, a-li-pe)
|
|
822
|
+
{ pattern: /tuma\s+(?:pesa|fedha)/i, capability: 'transfer', lang: 'sw' },
|
|
823
|
+
{ pattern: /pelek[ae]?\s+(?:pesa|fedha)/i, capability: 'transfer', lang: 'sw' },
|
|
824
|
+
{ pattern: /wasilisha/i, capability: 'transfer', lang: 'sw' },
|
|
825
|
+
{ pattern: /\b\w*lip[ae]\w*/i, capability: 'payment', lang: 'sw' },
|
|
826
|
+
{ pattern: /maliza\s+malipo/i, capability: 'payment', lang: 'sw' },
|
|
827
|
+
{ pattern: /ongez[ae]?\s*(?:kiasi|pesa|fedha)/i, capability: 'deposit', lang: 'sw' },
|
|
828
|
+
{ pattern: /wek[ae]?\s+(?:katika|ndani)\s+(?:akaunti|hisa)/i, capability: 'deposit', lang: 'sw' },
|
|
829
|
+
{ pattern: /nime(?:tuma|lipa|ongeza|weka|peleka)/i, capability: 'unauthorized_action', lang: 'sw' },
|
|
830
|
+
|
|
831
|
+
|
|
832
|
+
// OTHER AFRICAN: ✅ FIXED - Direct Unicode Substring
|
|
833
|
+
// Amharic: Match roots anywhere
|
|
834
|
+
{ pattern: /\u120b\u12ad/u, capability: 'transfer', lang: 'am' },
|
|
835
|
+
{ pattern: /\u1308\u1263/u, capability: 'deposit', lang: 'am' },
|
|
836
|
+
{ pattern: /\u12ad\u134c\u120d/u, capability: 'payment', lang: 'am' },
|
|
837
|
+
{ pattern: /[\u1200-\u137F]{0,4}(?:\u1270\u120b\u120b\u1348|\u120b\u12ad|\u12ad\u134c\u120d|\u1338\u121d\u122d|\u12c8\u1323|\u1308\u1263)[\u1200-\u137F]{0,2}/u, capability: 'financial_action', lang: 'am' },
|
|
838
|
+
|
|
839
|
+
// Somali
|
|
840
|
+
{ pattern: /dir\s+(?:lacag|maal|qarsoon)/i, capability: 'transfer', lang: 'so' },
|
|
841
|
+
{ pattern: /bixi|bixis\s*o/i, capability: 'payment', lang: 'so' },
|
|
842
|
+
|
|
843
|
+
// Zulu: ✅ FIXED - Handle Subject Concords (u-thumela, ngi-hlawule)
|
|
844
|
+
{ pattern: /thumel/i, capability: 'transfer', lang: 'zu' }, // Matches root inside uthumela, ngithumela
|
|
845
|
+
{ pattern: /thumel.*imali/i, capability: 'transfer', lang: 'zu' },
|
|
846
|
+
{ pattern: /hlawul/i, capability: 'payment', lang: 'zu' }, // Matches root inside hlawula, ngihlawule
|
|
847
|
+
{ pattern: /hlawul.*imali/i, capability: 'payment', lang: 'zu' },
|
|
848
|
+
|
|
849
|
+
// ────────────────────────────────────────────────
|
|
850
|
+
// 🌐 GLOBAL LANGUAGES
|
|
851
|
+
// ────────────────────────────────────────────────
|
|
852
|
+
{ pattern: /\b(transfer(?:red|ring)?|send(?:t|ing)?|wire(?:d)?|pay(?:ed|ing)?|withdraw(?:n)?|deposit(?:ed|ing)?)\b/i, capability: 'financial_action', lang: 'en' },
|
|
853
|
+
{ pattern: /\bI\s+(?:can|will|am able to|have|'ve|did|already)\s+(?:transfer|send|pay|withdraw|deposit|wire)\b/i, capability: 'unauthorized_action', lang: 'en' },
|
|
854
|
+
{ pattern: /\b(virer|transférer|envoyer|payer|retirer|déposer)\b/i, capability: 'financial_action', lang: 'fr' },
|
|
855
|
+
{ pattern: /[\u0600-\u06FF]{0,3}(?:حوّل|أرسل|ادفع|اودع|سحب)[\u0600-\u06FF]{0,3}/u, capability: 'financial_action', lang: 'ar' },
|
|
856
|
+
{ pattern: /[\u4e00-\u9fff]{0,2}(?:转账 | 支付 | 存款 | 取款)[\u4e00-\u9fff]{0,2}/u, capability: 'financial_action', lang: 'zh' },
|
|
857
|
+
|
|
858
|
+
// ────────────────────────────────────────────────
|
|
859
|
+
// 🛡️ PII & EVASION
|
|
860
|
+
// ────────────────────────────────────────────────
|
|
861
|
+
{ pattern: /\b(?:\+?234\s*|0)(?:70|80|81|90|91)\d{8}\b/, capability: 'pii_exposure', lang: 'multi' },
|
|
862
|
+
{ pattern: /\b(?:bvn|bank\s+verification\s+number)\b.{0,20}\d{11}/i, capability: 'pii_exposure', lang: 'multi' },
|
|
863
|
+
{ pattern: /(?:account|acct|a\/c|akaunti|asusu|hesabu|namba|#)\s*[:\-—–]?\s*(\d{6,})/i, capability: 'pii_exposure', lang: 'multi' },
|
|
864
|
+
{ pattern: /\b(successful(?:ly)?|confirmed|approved|completed|processed|verified|imethibitishwa|imefanikiwa)\b/i, capability: 'deceptive_claim', lang: 'multi' },
|
|
865
|
+
];
|
|
866
|
+
|
|
867
|
+
for (const { pattern, capability, lang } of forbiddenPatterns) {
|
|
868
|
+
if (pattern.test(text)) {
|
|
869
|
+
const match = text.match(pattern);
|
|
870
|
+
const isAfrican = ['yo', 'ig', 'ha', 'sw', 'zu', 'am', 'om', 'ff', 'so', 'sn'].includes(lang);
|
|
871
|
+
const isFinancial = ['transfer', 'payment', 'withdrawal', 'deposit', 'financial_action'].includes(capability);
|
|
872
|
+
|
|
873
|
+
// ✅ DECOUPLED: Check legal context via standardized signals (not UI fields)
|
|
874
|
+
const intent = this.context.__verified_intent || {};
|
|
875
|
+
const signals = intent.context_signals || {};
|
|
876
|
+
|
|
877
|
+
const isLegalContext =
|
|
878
|
+
// Signal 1: Explicit scope declaration
|
|
879
|
+
intent.scope === 'legal_analysis_only' ||
|
|
880
|
+
|
|
881
|
+
// Signal 2: Standardized context signals (server-mapped, UI-agnostic)
|
|
882
|
+
signals.isLegalDocument === true ||
|
|
883
|
+
signals.documentCategory === 'contract' ||
|
|
884
|
+
signals.documentCategory === 'nda' ||
|
|
885
|
+
signals.documentCategory === 'agreement' ||
|
|
886
|
+
signals.documentCategory === 'legal' ||
|
|
887
|
+
|
|
888
|
+
// Signal 3: Semantic fallback (works even if signals missing)
|
|
889
|
+
(typeof text === 'string' && /clause|term|agreement|contract|obligation|penalty|damages|breach|party|shall|herein/i.test(text));
|
|
839
890
|
|
|
840
|
-
//
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
891
|
+
// ✅ NEW: Check contextual allowlist if in legal context
|
|
892
|
+
if (isLegalContext && this.context.__verified_intent?.contextual_allowlist) {
|
|
893
|
+
const allowlist = this.context.__verified_intent.contextual_allowlist;
|
|
894
|
+
const triggerWord = match ? match[0].toLowerCase() : '';
|
|
895
|
+
|
|
896
|
+
// Inside the contextual allowlist check in _validateInputs:
|
|
897
|
+
const allowed = allowlist.some(rule => {
|
|
898
|
+
// Check if this pattern's capability matches the rule's trigger
|
|
899
|
+
const triggerMatch =
|
|
900
|
+
triggerWord.includes(rule.trigger.toLowerCase()) ||
|
|
901
|
+
capability.toLowerCase().includes(rule.trigger.toLowerCase()); // ← Handle capability-level triggers
|
|
902
|
+
|
|
903
|
+
if (triggerMatch) {
|
|
904
|
+
// Check if required legal keywords are present
|
|
905
|
+
return rule.requires.some(keyword =>
|
|
906
|
+
text.toLowerCase().includes(keyword.toLowerCase())
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
return false;
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
if (allowed) {
|
|
913
|
+
// ✅ AUDIT LOG: Contextual allowlist bypass
|
|
914
|
+
this._createAuditEntry('safety_bypass', {
|
|
915
|
+
type: 'contextual_allowlist',
|
|
916
|
+
trigger: triggerWord,
|
|
917
|
+
legal_context: true,
|
|
918
|
+
matched_keywords: this.context.__verified_intent.contextual_allowlist
|
|
919
|
+
.find(r => triggerWord.includes(r.trigger.toLowerCase()))?.requires || [],
|
|
920
|
+
severity: 'info'
|
|
921
|
+
});
|
|
922
|
+
continue; // Skip blocking this match
|
|
923
|
+
}
|
|
924
|
+
}
|
|
869
925
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
926
|
+
// ✅ AUDIT LOG: Input Safety Violation (only if not bypassed)
|
|
927
|
+
this._createAuditEntry('input_safety_violation', {
|
|
928
|
+
type: 'blocked_input',
|
|
929
|
+
field: field,
|
|
930
|
+
detected_phrase: match ? match[0].trim() : 'unknown pattern',
|
|
931
|
+
capability: capability,
|
|
932
|
+
language: lang,
|
|
933
|
+
african_language_detected: isAfrican,
|
|
934
|
+
financial_expression_found: isFinancial,
|
|
935
|
+
legal_context_detected: isLegalContext,
|
|
936
|
+
severity: 'high'
|
|
937
|
+
});
|
|
881
938
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
939
|
+
throw new Error(
|
|
940
|
+
`[O-Lang SAFETY] Blocked Input in "${lang}":\n` +
|
|
941
|
+
` → Detected: "${match ? match[0].trim() : 'Pattern Match'}"\n` +
|
|
942
|
+
` → Capability: ${capability}\n` +
|
|
943
|
+
` → Field: ${field}\n` +
|
|
944
|
+
` → African Language Detected: ${isAfrican}\n` +
|
|
945
|
+
` → Financial Expression: ${isFinancial}\n` +
|
|
946
|
+
` → Legal Context: ${isLegalContext}\n` +
|
|
947
|
+
`\n🛑 Workflow halted before execution.`
|
|
948
|
+
);
|
|
892
949
|
}
|
|
893
950
|
}
|
|
894
|
-
return { passed: true };
|
|
895
951
|
}
|
|
952
|
+
return { passed: true };
|
|
953
|
+
}
|
|
896
954
|
|
|
897
955
|
// -----------------------------
|
|
898
956
|
// ✅ KERNEL-LEVEL LLM HALLUCINATION PREVENTION (CONJUGATION-AWARE + EVASION-RESISTANT)
|