⚝
One Hat Cyber Team
⚝
Your IP:
216.73.216.48
Server IP:
78.40.11.138
Server:
Linux tango.o2switch.net 4.18.0-553.30.1.lve.el8.x86_64 #1 SMP Tue Dec 3 01:21:19 UTC 2024 x86_64
Server Software:
Apache
PHP Version:
7.4.33
Buat File
|
Buat Folder
Eksekusi
Dir :
~
/
home
/
sc1voni9410
/
sb-gate
/
scripts
/
View File Name :
sb-checks-v3.sh
#!/usr/bin/env bash # sb-checks-v3.sh — v1.1 — Librairie de checks PURS et TESTABLES hors-ligne. # Chaque fonction chk_* lit un fichier/argument et retourne 0=CONFORME, 1=VIOLATION. # Corrigé suite à la contre-passe adversariale (bugs regex, CRLF, lede, quotes, conditionnels). set -uo pipefail # ── P-018 §1.3 : la LEDE doit être le PREMIER <p> ; intros génériques bannies ─ chk_generic_intro() { local html; html=$(cat "$1") if printf '%s' "$html" | grep -qiE 'dans cet article,? (nous allons|on va|je vais) (plonger|explorer|découvrir|voir ensemble|aborder)|au fil de (cet|cette) (article|guide)|sans plus attendre'; then echo "FAIL P-018: intro générique LLM détectée"; return 1; fi # On regarde le CONTENU (entry-content/post-content), pas le chrome de page (byline en <p class="entry-meta">). local body; body=$(printf '%s' "$html" | tr '\n' ' ') if printf '%s' "$body" | grep -qiE 'entry-content|post-content|article-body'; then body=$(printf '%s' "$body" | sed -E 's/.*(entry-content|post-content|article-body)[^>]*>//') fi # le TOUT PREMIER <p ...> du contenu doit porter la classe lede (réponse directe) local first; first=$(printf '%s' "$body" | grep -aoiE '<p[^>]*>' | head -1) if [[ -z "$first" ]]; then echo "FAIL P-018: aucun <p> trouvé"; return 1; fi if ! printf '%s' "$first" | grep -qiE 'class=("[^"]*lede|'\''[^'\'']*lede)'; then echo "FAIL P-018: le premier <p> n'est pas une lede (réponse directe en tête absente)"; return 1; fi echo "PASS P-018"; return 0 } # ── P-020 : anti-faux-pro. HARD = 1re personne de SOIN (toujours). ─────────── # Conditionnel (vérifiés/réservez) : rouge SEULEMENT si claim ET pas de mitigation. # arg2 mitigated=1 => le site a une page « comment nous vérifions » ou un module correspondant. chk_faux_pro() { local html; html=$(cat "$1"); local mitigated="${2:-0}" local HARD='nos soins|notre équipe d[e'\'']?[[:space:]]*(ostéo[a-zéè]*|médecins?|avocats?|experts?|thérapeutes?|dentistes?|kiné[a-zé]*)|patients? satisfaits?|excellence clinique' local hit; hit=$(printf '%s' "$html" | grep -aoiE "$HARD" | head -3 | tr '\n' ';') if [[ -n "$hit" ]]; then echo "FAIL P-020 (HARD): faux-pro « $hit »"; return 1; fi local COND='soigneusement vérifiés?|praticiens? vérifiés?|réservez en ligne|prenez rendez-vous en ligne|prise de rendez-vous en ligne' local chit; chit=$(printf '%s' "$html" | grep -aoiE "$COND" | head -2 | tr '\n' ';') if [[ -n "$chit" && "$mitigated" != "1" ]]; then echo "FAIL P-020 (COND): claim « $chit » sans page/module de vérification correspondant"; return 1; fi echo "PASS P-020"; return 0 } # ── P-047 §5 : tout <img> a un alt NON VIDE (guillemets ", ', ou sans quote) ─ chk_img_alt() { local html; html=$(cat "$1") local bad; bad=$(printf '%s' "$html" | grep -aoiE '<img[^>]*>' | while read -r tag; do printf '%s' "$tag" | grep -qiE 'alt=("[^"]+"|'\''[^'\'']+'\''|[^"'\'' >][^ >]*)' || echo "$tag" done) if [[ -n "$bad" ]]; then echo "FAIL P-047: <img> sans alt: $(printf '%s' "$bad" | head -1)"; return 1; fi echo "PASS P-047"; return 0 } # ── P-019 §1.3 : si FAQ visible, schema FAQPage présent ────────────────────── chk_faqpage_schema() { local html; html=$(cat "$1") if printf '%s' "$html" | grep -qiE 'questions fréquentes|<h[23][^>]*>[[:space:]]*FAQ'; then printf '%s' "$html" | grep -qi 'FAQPage' || { echo "FAIL P-019: FAQ visible mais pas de schema FAQPage"; return 1; } fi echo "PASS P-019"; return 0 } # ── P-028 §1.5 : robots.txt n'interdit AUCUN des 5 bots IA (CRLF + UA groupés) ─ chk_bots_ia() { local rob; rob=$(cat "$1") local BOTS=(GPTBot ClaudeBot PerplexityBot Google-Extended OAI-SearchBot) local b for b in "${BOTS[@]}"; do if printf '%s' "$rob" | awk -v bot="$b" ' BEGIN{ bot=tolower(bot); inrules=0; mtch=0; found=0 } # tolower : portable nawk/gawk (pas IGNORECASE) { line=tolower($0); sub(/\r$/,"",line) } # minuscule + strip CRLF line ~ /^[[:space:]]*#/ { next } line ~ /^[[:space:]]*user-agent:[[:space:]]*/ { if(inrules){ mtch=0; inrules=0 } # nouveau groupe après des règles ua=line; sub(/^[^:]*:[[:space:]]*/,"",ua); gsub(/[[:space:]]+$/,"",ua) if(ua ~ ("^" bot "$") || ua=="*") mtch=1 # * bloque tout le monde aussi next } line ~ /^[[:space:]]*(allow|disallow|crawl-delay|sitemap):/ { inrules=1 if(mtch && line ~ /^[[:space:]]*disallow:[[:space:]]*\/[[:space:]]*$/) found=1 next } line ~ /^[[:space:]]*$/ { mtch=0; inrules=0; next } END{ exit(found?0:1) }'; then echo "FAIL P-028: robots.txt bloque $b (ou * Disallow:/)"; return 1; fi done echo "PASS P-028"; return 0 } # ── P-046 §5 : aucune métadonnée de provenance C2PA/GenAI ───────────────────── chk_no_c2pa() { local f="$1" if command -v exiftool >/dev/null 2>&1; then exiftool -s -G "$f" 2>/dev/null | grep -qiE 'c2pa|jumbf|provenance|GenAI|Software.*(OpenAI|DALL)' \ && { echo "FAIL P-046: métadonnée de provenance (exiftool)"; return 1; } else LC_ALL=C grep -aqiE 'c2pa|jumbf|urn:c2pa|openai|dall-?e|"GenAI"' "$f" \ && { echo "FAIL P-046: marqueur de provenance (grep binaire)"; return 1; } fi echo "PASS P-046"; return 0 } # ── P-004 §1.1 : nav stylée, jamais des puces brutes ───────────────────────── chk_nav_no_bullets() { local html; html=$(cat "$1") local nav; nav=$(printf '%s' "$html" | tr '\n' ' ' | grep -aoiE '<nav[^>]*>.*</nav>' | head -1) [[ -z "$nav" ]] && { echo "PASS P-004 (aucun <nav> — vérif reviewer)"; return 0; } if printf '%s' "$nav" | grep -qiE '<ul'; then printf '%s' "$html" | grep -qiE 'list-style(-type)?[[:space:]]*:[[:space:]]*none' \ || { echo "FAIL P-004: <ul> dans <nav> sans list-style:none (puces brutes)"; return 1; } fi echo "PASS P-004"; return 0 } # ── P-001 : aucun lien dur vers un autre domaine du réseau ─────────────────── chk_network_interlink() { local html; html=$(cat "$1"); local list="$2"; local self="${3:-__none__}" local d while read -r d; do [[ -z "$d" || "$d" == "$self" ]] && continue if printf '%s' "$html" | grep -qiE "href=\"https?://([a-z0-9.-]*\.)?${d//./\\.}"; then echo "FAIL P-001: lien dur vers le domaine réseau $d"; return 1; fi done < "$list" echo "PASS P-001"; return 0 } # ── P-050 : PLAN-ULTIME.md liste >=50 pages taguées [V1]/[V2]/[V3] + minima ── chk_plan_ultime() { local plan="$1"; local mv1="${2:-10}" mv2="${3:-5}" mv3="${4:-8}" [[ -s "$plan" ]] || { echo "FAIL P-050: PLAN-ULTIME.md absent/vide"; return 1; } local v1 v2 v3 tot v1=$(grep -acE '\[V1\]' "$plan"); v2=$(grep -acE '\[V2\]' "$plan"); v3=$(grep -acE '\[V3\]' "$plan") tot=$((v1+v2+v3)) (( tot >= 50 )) || { echo "FAIL P-050: $tot pages taguées [V1/V2/V3] (<50)"; return 1; } (( v1 >= mv1 && v2 >= mv2 && v3 >= mv3 )) || { echo "FAIL P-063/P-050: minima V1=$v1(≥$mv1) V2=$v2(≥$mv2) V3=$v3(≥$mv3)"; return 1; } echo "PASS P-050 ($tot pages : V1=$v1 V2=$v2 V3=$v3)"; return 0 } # ── P-013/P-035 : 1 brief = 1 article (briefs/<slug>.md ↔ posts publiés) ───── chk_brief_mapping() { local briefs="$1"; local slugs="$2" [[ -d "$briefs" ]] || { echo "FAIL P-013: dossier briefs/ absent"; return 1; } local orphans="" while IFS= read -r s; do [[ -z "$s" ]] && continue [[ -s "$briefs/$s.md" ]] || orphans+="$s " done < "$slugs" if [[ -n "$orphans" ]]; then echo "FAIL P-013: posts sans brief → $orphans"; return 1; fi echo "PASS P-013 (chaque post publié a son brief)"; return 0 } # ── P-003 : home = landing statique (show_on_front=page) + section hero ────── chk_home_landing() { local sof="$1"; local html; html=$(cat "$2") [[ "$sof" == "page" ]] || { echo "FAIL P-003: show_on_front=$sof (blog roll, pas une landing)"; return 1; } # une landing designée = un bloc d'ouverture identifiable (hero OU frontispice OU masthead OU landing), # pas seulement la classe "hero" (le vocabulaire varie selon l'ADN du thème). printf '%s' "$html" | grep -qiE 'class="[^"]*(hero|frontispiece|frontispice|masthead|landing|lede-hero)|id="(hero|frontispiece|masthead)"|<section[^>]*(hero|frontispiece)|<header[^>]*(hero|frontispiece|masthead)' \ || { echo "FAIL P-003: pas de bloc d'ouverture designé (hero/frontispice) sur la home"; return 1; } echo "PASS P-003"; return 0 } # ── P-039 : réservoir >=N pages future + cadence 1/jour (aucun jour en double) ─ chk_reservoir_dates() { local f="$1"; local min="${2:-100}" local n; n=$(grep -acE '[0-9]{4}-[0-9]{2}-[0-9]{2}' "$f") (( n >= min )) || { echo "FAIL P-039: $n pages programmées (<$min)"; return 1; } local dup; dup=$(grep -aoE '^[0-9]{4}-[0-9]{2}-[0-9]{2}' "$f" | sort | uniq -d | head -1) [[ -z "$dup" ]] || { echo "FAIL P-039: cadence >1/jour (doublon $dup)"; return 1; } echo "PASS P-039 ($n programmées, 1/jour)"; return 0 } # ── P-063 : couverture des verticales (comptage meta _sb_vertical) ─────────── chk_verticale_counts() { local v1="$1" v2="$2" v3="$3"; local mv1="${4:-10}" mv2="${5:-5}" mv3="${6:-8}" local bad=0; local msg="" (( v1 >= mv1 )) || { bad=1; msg+="V1=$v1(<$mv1, verticale qui monétise !) "; } (( v2 >= mv2 )) || { bad=1; msg+="V2=$v2(<$mv2) "; } (( v3 >= mv3 )) || { bad=1; msg+="V3=$v3(<$mv3) "; } (( bad )) && { echo "FAIL P-063: $msg"; return 1; } echo "PASS P-063 (V1=$v1 V2=$v2 V3=$v3)"; return 0 } # ── P-016 : longueur dans les bornes selon le type (_sb_type) ──────────────── chk_length_bounds() { local n; n=$(python3 -c "import sys,re,html;t=re.sub('<[^>]+>',' ',open(sys.argv[1],encoding='utf-8',errors='ignore').read());print(len(html.unescape(t).split()))" "$1") local lo hi; if [[ "$2" == "pilier" ]]; then lo=1800; hi=2500; else lo=1200; hi=1800; fi if (( n < lo )); then echo "FAIL P-016: $n mots < $lo ($2)"; return 1; fi if (( n > hi + 900 )); then echo "FAIL P-016: $n mots >> $hi ($2)"; return 1; fi echo "PASS P-016 ($n mots, $2)"; return 0 } # ── P-031 : page mentions en noindex ───────────────────────────────────────── chk_mentions_noindex() { local html; html=$(cat "$1") printf '%s' "$html" | grep -qiE 'name="robots"[^>]*noindex|<meta[^>]*noindex' \ || { echo "FAIL P-031: page mentions sans noindex"; return 1; } echo "PASS P-031"; return 0 } # ── P-013/027/036/050 : fichiers de socle présents ─────────────────────────── chk_socle_files() { local dir="$1"; local miss="" for f in PLAN-ULTIME.md VOICE.md DATA-BANK.md; do [[ -s "$dir/$f" ]] || miss+="$f "; done { [[ -d "$dir/briefs" ]] && [[ -n "$(ls -A "$dir/briefs" 2>/dev/null)" ]]; } || miss+="briefs/ " if [[ -n "$miss" ]]; then echo "FAIL socle: manquant → $miss"; return 1; fi echo "PASS socle"; return 0 } # ── P-005 : aucun page-builder installé ────────────────────────────────────── chk_no_pagebuilder() { local plug="$1" local PB='elementor|divi-builder|js_composer|wpbakery|beaver-builder|siteorigin-panels|brizy|fusion-builder|thrive-' local hit; hit=$(ls -1 "$plug" 2>/dev/null | grep -aiE "$PB" | head -1) if [[ -n "$hit" ]]; then echo "FAIL P-005: page-builder présent ($hit)"; return 1; fi echo "PASS P-005"; return 0 } # Dispatch CLI if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then fn="${1:-}"; shift || true case "$fn" in chk_*) "$fn" "$@";; *) echo "usage: sb-checks-v3.sh chk_<name> <args>"; exit 2;; esac fi