• Security incident: ISF was recently accessed by intruders. Please change your password, and change it anywhere else you used it. Read more

Fake science is out of control

About the only relevant thing you may need AI for is to write the program. Then that program would not hallucinate.

Why would you need AI to write the program in the first place?
 
Thought I'd see whether an AI could code something to do this. It could - a few problems along the way, mostly because of me insisting it all be contained in a single HTML file.

If you want to play with it copy the code into an editor that can save it as plain text, save the file as "citationchecker.html" (or any name you want) and drop the file into your browser, only works with mainly text PDFs.

HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Darat -  Universal Citation‑Miner</title>

<style>
  :root {
    --bg-sage: #E8EDD9;
    --panel-bg: #FAF7F2;
    --text-main: #2F2F2F;
    --olive: #6B7A3A;
    --terracotta: #C46A4A;
    --line-soft: #D9DCC8;
    --badge-bg: #333333;
    --badge-text: #FFFFFF;
  }

  body {
    margin: 0;
    padding: 24px;
    background: var(--bg-sage);
    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    color: var(--text-main);
  }

  h1, h2 {
    font-family: Georgia, "Times New Roman", serif;
    margin: 0 0 12px 0;
    color: var(--text-main);
  }

  h1 { font-size: 26px; letter-spacing: 0.03em; }
  h2 { font-size: 18px; letter-spacing: 0.02em; }

  .panel {
    background: var(--panel-bg);
    border-radius: 14px;
    padding: 18px 20px;
    margin-bottom: 20px;
    box-shadow: 0 10px 24px rgba(0,0,0,0.06);
    border: 1px solid rgba(0,0,0,0.03);
  }

  .pill {
    display: inline-flex;
    align-items: center;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 11px;
    background: #E1E6CF;
    color: var(--text-main);
  }

  button {
    border-radius: 999px;
    border: none;
    padding: 8px 18px;
    margin-left: 10px;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    background: var(--olive);
    color: #fff;
    transition: background 0.15s ease, transform 0.1s ease;
  }

  button:hover:not(:disabled) {
    background: #7C8B45;
    transform: translateY(-1px);
  }

  .accordion {
    border-radius: 10px;
    overflow: hidden;
    margin-bottom: 10px;
    border: 1px solid rgba(0,0,0,0.05);
  }

  .accordion-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 14px;
    background: #F2F4E4;
    cursor: pointer;
  }

  .accordion-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    font-weight: 600;
    text-transform: uppercase;
  }

  .badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 24px;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    background: var(--badge-bg);
    color: var(--badge-text);
  }

  .accordion-body {
    display: none;
    padding: 10px 14px 14px 14px;
    background: #FBFAF6;
  }

  .accordion-body.open { display: block; }

  table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 6px;
    font-size: 13px;
  }

  th, td {
    padding: 10px 10px;
    border-bottom: 1px solid var(--line-soft);
    vertical-align: top;
  }

  th {
    text-align: left;
    font-size: 12px;
    text-transform: uppercase;
    color: #555;
  }

  tbody tr:nth-child(odd) { background: #FBFAF6; }
  tbody tr:nth-child(even) { background: #F5F6EB; }

  .status-ok { color: var(--olive); font-weight: 600; }
  .status-fail { color: var(--terracotta); font-weight: 600; }
  .confidence-high { color: var(--olive); font-weight: 600; }
  .confidence-medium { color: var(--terracotta); font-weight: 600; }

  a.doi-link {
    color: var(--olive);
    text-decoration: none;
    font-size: 12px;
  }
</style>
</head>
<body>

<div class="panel">
  <div style="display:flex; justify-content:space-between; align-items:flex-start; gap:12px; flex-wrap:wrap;">
    <div>
      <h1>Darat's Universal Citation‑Miner</h1>
      <div style="font-size:13px; max-width:520px;">
        Extract full references, inline citations, numeric citations, DOIs, footnotes, and more from any PDF.
      </div>
    </div>
    <div style="display:flex; flex-direction:column; align-items:flex-end; gap:6px;">
      <span class="pill">Worker‑free · Local‑friendly</span>
      <span class="pill">CrossRef + OpenAlex</span>
    </div>
  </div>

  <div style="margin-top:14px;">
    <label style="font-size:14px; font-weight:500;">
      Load PDF:
      <input type="file" id="pdfFile" accept="application/pdf">
    </label>
    <button id="extractBtn" disabled>Mine Citations</button>
  </div>

  <div id="status" style="margin-top:10px; font-size:13px;"></div>
  <pre id="error-log" style="margin-top:8px; font-size:12px; color:#C46A4A; white-space:pre-wrap; max-height:140px; overflow-y:auto;"></pre>
</div>

<div class="panel" id="results-panel" style="display:none;">
  <h2>Citations</h2>

  <div id="accordion-full" class="accordion">
    <div class="accordion-header">
      <div class="accordion-title">
        <span>Full References</span>
        <span class="badge" id="badge-full">0</span>
      </div>
      <div>▼</div>
    </div>
    <div class="accordion-body" id="body-full"></div>
  </div>

  <div id="accordion-inline" class="accordion">
    <div class="accordion-header">
      <div class="accordion-title">
        <span>Inline Citations</span>
        <span class="badge" id="badge-inline">0</span>
      </div>
      <div>▼</div>
    </div>
    <div class="accordion-body" id="body-inline"></div>
  </div>

  <div id="accordion-numeric" class="accordion">
    <div class="accordion-header">
      <div class="accordion-title">
        <span>Numeric Citations</span>
        <span class="badge" id="badge-numeric">0</span>
      </div>
      <div>▼</div>
    </div>
    <div class="accordion-body" id="body-numeric"></div>
  </div>

  <div id="accordion-doi" class="accordion">
    <div class="accordion-header">
      <div class="accordion-title">
        <span>DOIs</span>
        <span class="badge" id="badge-doi">0</span>
      </div>
      <div>▼</div>
    </div>
    <div class="accordion-body" id="body-doi"></div>
  </div>

  <div id="accordion-footnote" class="accordion">
    <div class="accordion-header">
      <div class="accordion-title">
        <span>Footnotes</span>
        <span class="badge" id="badge-footnote">0</span>
      </div>
      <div>▼</div>
    </div>
    <div class="accordion-body" id="body-footnote"></div>
  </div>

  <div id="accordion-other" class="accordion">
    <div class="accordion-header">
      <div class="accordion-title">
        <span>Other / Unclassified</span>
        <span class="badge" id="badge-other">0</span>
      </div>
      <div>▼</div>
    </div>
    <div class="accordion-body" id="body-other"></div>
  </div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js"></script>
<script>
let pdfArrayBuffer = null;

const fileInput = document.getElementById("pdfFile");
const extractBtn = document.getElementById("extractBtn");
const statusEl = document.getElementById("status");
const errorLogEl = document.getElementById("error-log");
const resultsPanel = document.getElementById("results-panel");

const badgeFull = document.getElementById("badge-full");
const badgeInline = document.getElementById("badge-inline");
const badgeNumeric = document.getElementById("badge-numeric");
const badgeDoi = document.getElementById("badge-doi");
const badgeFootnote = document.getElementById("badge-footnote");
const badgeOther = document.getElementById("badge-other");

const bodyFull = document.getElementById("body-full");
const bodyInline = document.getElementById("body-inline");
const bodyNumeric = document.getElementById("body-numeric");
const bodyDoi = document.getElementById("body-doi");
const bodyFootnote = document.getElementById("body-footnote");
const bodyOther = document.getElementById("body-other");

function logError(msg, err) {
  errorLogEl.textContent += msg + (err ? " :: " + err : "") + "\n";
  console.error(msg, err);
}

/* -----------------------------------------
   ACCORDION BEHAVIOUR
------------------------------------------*/
document.querySelectorAll(".accordion-header").forEach(header => {
  header.addEventListener("click", () => {
    const body = header.nextElementSibling;
    body.classList.toggle("open");
  });
});

/* -----------------------------------------
   FILE LOADING
------------------------------------------*/
fileInput.addEventListener("change", async e => {
  const file = e.target.files[0];
  if (!file) return;
  try {
    pdfArrayBuffer = await file.arrayBuffer();
    extractBtn.disabled = false;
    statusEl.textContent = "PDF loaded.";
    errorLogEl.textContent = "";
    resultsPanel.style.display = "none";
  } catch (err) {
    logError("Error reading file", err);
  }
});

/* -----------------------------------------
   EXTRACTION WORKFLOW
------------------------------------------*/
extractBtn.addEventListener("click", async () => {
  if (!pdfArrayBuffer) return;

  extractBtn.disabled = true;
  statusEl.textContent = "Extracting text…";
  errorLogEl.textContent = "";
  resultsPanel.style.display = "none";

  try {
    const pdf = await pdfjsLib.getDocument({ data: pdfArrayBuffer }).promise;
    let pages = [];

    for (let i = 1; i <= pdf.numPages; i++) {
      const page = await pdf.getPage(i);
      const content = await page.getTextContent();
      const text = content.items.map(x => x.str).join(" ");
      pages.push(text);
    }

    const cleaned = cleanHeaders(pages);
    statusEl.textContent = "Mining citations…";

    const mined = mineCitations(cleaned);
    await verifyCitations(mined);

    renderGroups(mined);
    resultsPanel.style.display = "block";
    statusEl.textContent = "Done.";

  } catch (err) {
    logError("Extraction error", err);
    statusEl.textContent = "Error during extraction.";
  }

  extractBtn.disabled = false;
});

/* -----------------------------------------
   HEADER CLEANER
------------------------------------------*/
function cleanHeaders(pages) {
  const heads = pages.map(p => p.slice(0, 120).trim());
  const freq = {};

  heads.forEach(h => {
    if (h.length < 20) return;
    const key = h.slice(0, 60);
    freq[key] = (freq[key] || 0) + 1;
  });

  let header = null;
  let max = 0;
  for (const k in freq) {
    if (freq[k] > max && freq[k] >= 3) {
      max = freq[k];
      header = k;
    }
  }

  let text = pages.join("\n\n");

  if (header) {
    const safe = header.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
    const re = new RegExp(safe + ".*?(?=\\n|$)", "g");
    text = text.replace(re, "");
  }

  text = text.replace(/Page Proof.*?(?=\n|$)/gi, "");
  text = text.replace(/Compositor Name:.*?(?=\n|$)/gi, "");
  text = text.replace(/\bpage\s+\d+\b/gi, "");
  text = text.replace(/\b\d{1,2}\.\d{1,2}\.\d{2,4}\b/g, "");
  text = text.replace(/\b\d{1,2}:\d{2}(am|pm)\b/gi, "");

  return text.replace(/\s+/g, " ").trim();
}

/* -----------------------------------------
   CITATION MINER
------------------------------------------*/
function mineCitations(text) {
  const fullRefs = new Set();
  const inlineCits = new Set();
  const numericCits = new Set();
  const doiCits = new Set();
  const footnoteCits = new Set();
  const otherCits = new Set();

  function accept(str) {
    const s = str.trim();
    if (s.length < 25 || s.length > 350) return false;
    const bad = ["page proof", "compositor", "copyright", "printed in"];
    return !bad.some(w => s.toLowerCase().includes(w));
  }

  const doiRegex = /10\.\d{4,9}\/[-._;()/:A-Za-z0-9]+/g;
  let m;
  while ((m = doiRegex.exec(text)) !== null) doiCits.add(m[0]);

  const sentences = text.split(/(?<=[.!?])\s+(?=[A-Z])/g);

  const fullRefRegex = /[A-Z][a-z]+,\s+[A-Z](?:\.[A-Z]\.)?.*?\b(19|20)\d{2}.*?(?=[.!?])/g;
  sentences.forEach(s => {
    let mm;
    while ((mm = fullRefRegex.exec(s)) !== null) {
      if (accept(mm[0])) fullRefs.add(mm[0]);
    }
  });

  const inline1 = /\([A-Z][A-Za-z]+(?:\s+et al\.)?,\s*(19|20)\d{2}\)/g;
  const inline2 = /[A-Z][A-Za-z]+(?:\s+et al\.)?\s*\((19|20)\d{2}\)/g;
  sentences.forEach(s => {
    let mm;
    while ((mm = inline1.exec(s)) !== null) if (accept(mm[0])) inlineCits.add(mm[0]);
    while ((mm = inline2.exec(s)) !== null) if (accept(mm[0])) inlineCits.add(mm[0]);
  });

  const numRegex = /\[\d+(?:\s*,\s*\d+)*\]/g;
  sentences.forEach(s => {
    let mm;
    while ((mm = numRegex.exec(s)) !== null) if (accept(mm[0])) numericCits.add(mm[0]);
  });

  const footRegex = /\b\d{1,3}\s+[A-Z][a-z]+.*?\b(19|20)\d{2}.*?(?=[.!?])/g;
  sentences.forEach(s => {
    let mm;
    while ((mm = footRegex.exec(s)) !== null) if (accept(mm[0])) footnoteCits.add(mm[0]);
  });

  sentences.forEach(s => {
    const hasYear = /(19|20)\d{2}/.test(s);
    const hasName = /[A-Z][a-z]+,\s+[A-Z]\./.test(s);
    if (hasYear && hasName && accept(s)) {
      if (![...fullRefs].includes(s) && ![...footnoteCits].includes(s)) {
        otherCits.add(s);
      }
    }
  });

  return {
    full: [...fullRefs],
    inline: [...inlineCits],
    numeric: [...numericCits],
    doi: [...doiCits],
    footnote: [...footnoteCits],
    other: [...otherCits]
  };
}

/* -----------------------------------------
   VERIFICATION (CrossRef + OpenAlex)
------------------------------------------*/
async function verifyCitations(mined) {
  mined._verifiedFull = [];
  mined._verifiedDoi = [];

  for (const ref of mined.full) {
    const res = await checkReference(ref);
    mined._verifiedFull.push({ reference: ref, ...res });
  }

  for (const doi of mined.doi) {
    const res = await checkReference(doi);
    mined._verifiedDoi.push({ reference: doi, ...res });
  }
}

function extractPossibleDOI(ref) {
  const m = ref.match(/10\.\d{4,9}\/[-._;()/:A-Za-z0-9]+/);
  return m ? m[0] : null;
}

async function checkReference(ref) {
  let doi = extractPossibleDOI(ref);
  let sources = [];
  let score = 0;

  let cross = null;
  let alex = null;

  try {
    if (doi) {
      const r = await fetch("https://api.crossref.org/works/" + encodeURIComponent(doi));
      if (r.ok) cross = (await r.json()).message;
    }
    if (!cross) {
      const q = encodeURIComponent(ref.slice(0, 200));
      const r = await fetch("https://api.crossref.org/works?query.bibliographic=" + q + "&rows=1");
      if (r.ok) {
        const d = await r.json();
        if (d.message.items.length) cross = d.message.items[0];
      }
    }
  } catch {}

  if (cross) {
    sources.push("CrossRef");
    if (!doi && cross.DOI) doi = cross.DOI;
    score += 0.4;
  }

  try {
    const q = encodeURIComponent(ref.slice(0, 200));
    const r = await fetch("https://api.openalex.org/works?search=" + q + "&per-page=1");
    if (r.ok) {
      const d = await r.json();
      if (d.results.length) alex = d.results[0];
    }
  } catch {}

  if (alex) {
    sources.push("OpenAlex");
    if (!doi && alex.doi) doi = alex.doi.replace(/^https?:\/\/doi.org\//, "");
    score += 0.3;
  }

  const status = score >= 0.4 ? "ok" : "fail";
  const confidence = score >= 0.75 ? "High" : score >= 0.4 ? "Medium" : "Low";

  return { status, confidence, doi, sources, score };
}

/* -----------------------------------------
   RENDERING
------------------------------------------*/
function escapeHtml(str) {
  return str
    .replace(/&/g,"&amp;")
    .replace(/</g,"&lt;")
    .replace(/>/g,"&gt;");
}

function renderGroups(mined) {
  badgeFull.textContent = mined._verifiedFull.length;
  bodyFull.innerHTML = renderVerifiedTable(mined._verifiedFull);

  badgeInline.textContent = mined.inline.length;
  bodyInline.innerHTML = renderSimpleTable(mined.inline);

  badgeNumeric.textContent = mined.numeric.length;
  bodyNumeric.innerHTML = renderSimpleTable(mined.numeric);

  badgeDoi.textContent = mined._verifiedDoi.length;
  bodyDoi.innerHTML = renderVerifiedTable(mined._verifiedDoi);

  badgeFootnote.textContent = mined.footnote.length;
  bodyFootnote.innerHTML = renderSimpleTable(mined.footnote);

  badgeOther.textContent = mined.other.length;
  bodyOther.innerHTML = renderSimpleTable(mined.other);
}

function renderVerifiedTable(items) {
  if (!items.length) {
    return '<div style="font-size:12px; color:#666;">No items detected.</div>';
  }

  let html = `
    <table>
      <thead>
        <tr>
          <th>Status</th>
          <th>Confidence</th>
          <th>Reference</th>
          <th>DOI</th>
          <th>Sources</th>
        </tr>
      </thead>
      <tbody>
  `;

  items.forEach(r => {
    const statusClass =
      r.status === "ok" ? "status-ok" :
      r.status === "fail" ? "status-fail" : "";

    const confClass =
      r.confidence === "High" ? "confidence-high" :
      r.confidence === "Medium" ? "confidence-medium" : "";

    let doiHtml = "—";
    if (r.doi) {
      const url = "https://doi.org/" + r.doi;
      doiHtml = `<a class="doi-link" href="${url}" target="_blank">${escapeHtml(r.doi)}</a>`;
    }

    const src = r.sources && r.sources.length ? r.sources.join(" + ") : "—";

    html += `
      <tr>
        <td class="${statusClass}">${escapeHtml(r.status)}</td>
        <td class="${confClass}">${escapeHtml(r.confidence)}</td>
        <td>${escapeHtml(r.reference)}</td>
        <td>${doiHtml}</td>
        <td>${escapeHtml(src)}</td>
      </tr>
    `;
  });

  html += "</tbody></table>";
  return html;
}

function renderSimpleTable(list) {
  if (!list.length) {
    return '<div style="font-size:12px; color:#666;">No items detected.</div>';
  }

  let html = `
    <table>
      <thead><tr><th>Citation</th></tr></thead>
      <tbody>
  `;

  list.forEach(item => {
    html += `<tr><td>${escapeHtml(item)}</td></tr>`;
  });

  html += "</tbody></table>";
  return html;
}
</script>

</body>
</html>
I tried it on 3 journal articles in pdf form. It finds only a small fraction of the references, prints out only a portion of those it finds, and rates them all "medium."
 
A more interesting use of AI would be, IMO, to have AI read a paper and the references cited therein and assess whether the claims the paper makes for each cited reference are supported by the reference.
 
Last edited:
Last edited:
Testing is so pre-AI, get with the vibe!
This is one of the things I don't understand about AI-based software development. There's nothing about it that says "no testing needed". It's never been marketed that way. Code reviews and testing have always been part of software development, and AI doesn't change that.

I can understand a conference room full of startup bros thinking they can save on labor costs by using AI. What I can't understand is mature shops like Amazon getting buggy code into production like this. Why skip testing entirely for no reason?
 
So I downloaded "Enhancing Network Security through Accurate Asset Discovery Using Lightweight Agent-Based Approach", published in the Journal of Computer Science. I asked Copilot if "the claims the paper makes about each cited reference are supported by the reference" (jt512's prompt verbatim).

It came back with a table evaluating each cited source. The first three entries are below. It's interesting that it found a bad reference in the first three cites. I haven't checked any of Copilot's work, though. I have no idea how accurate these assessments actually are, or whether the citations it's assessing are even in the paper.

In the original output, Copilot did indicate the third-party source it used to make its assessment. My understanding is that the source is where Copilot found the paper being cited, or at least its abstract.



Reference: Ahmed Abdirahman et al., 2024 – IJECE 14(4)
Claim in the Paper: Claimed to predict vulnerability severity using NLP, providing a method to assess potential threats.
Actual Reference Content: The referenced article indeed applies NLP and deep learning to predict vulnerability severity from text descriptions, introducing a model for assessing vulnerabilities.
Assessment: Supported – The claim matches the reference’s focus on NLP-based vulnerability severity prediction.

---

Reference: Ahmed & Khorsheed, 2022 – IJEECS 27(1)
Claim in the Paper: Cited as discussing open network structures and smart networks in 5G, supporting the need for scalable, flexible security solutions.
Actual Reference Content: The paper examines open 5G network architectures and cybersecurity sharing, emphasizing scalable, flexible security approaches for 5G networks.
Assessment: Supported – The reference indeed advocates for flexible, scalable cybersecurity in 5G, aligning with the claim.

---

Reference: Ajiboye et al., 2023 – IMECS Proc.
Claim in the Paper: Referenced regarding “the accuracy of the provided seed and rule” determining network discovery authenticity.
Actual Reference Content: The actual work is about dimensionality reduction for IoT intrusion detection, not seed/rule accuracy in network scans. It does not address seed/rule “authenticity” for discovery.
Assessment: Not Supported – The citation is misapplied; Ajiboye et al. (2023) is unrelated to seed/rule accuracy in network
 
I've also been using Claude agent models a lot lately. Claude tends to be very intuitive, and makes a lot of inferences. For software development, this means inferring why I want a particular piece of code, and asking me follow up questions about whether I want certain features or functionality that usually come along with code written for that purpose. It's usually correct, which is kind of creepy.

Which makes me think it can't be too implausible for a "researcher" agent persona to infer from a request for citations a requirement that the references found should be checked for relevance to the claim. Or at least for actual existence.

In fact, I wouldn't be surprised if you could get acceptable results from ChatGPT, just by including a few more clauses in your prompt: "Find references supporting proposition X. Verify that each reference exists and you can read it. Read each reference, and determine whether it actually supports proposition X. Properly cite each supporting reference. Check each citation for correctness. The end result should be a list of verified citations to references supporting proposition X. Show me the end result."

That's the kind of prompt I would write. Not just, "write a legal brief in favor of proposition X, complete with citations."

Then I'd tell the agent to write the brief or whatever document, based on the support for X in the verified references. A lot of my code projects these days start that way. Read this architecture document, this readme, and this code repository. Create a new code repository based on the first one, and wire it up according to the architecture and readme documents.

So I do know that (some) agents can read source material, and infer rules and requirements for kicking off a new body of work along the same lines. If an agent can do it with code, it can do it with legal references.
 
Last edited:

ISF - Join now!

Every member here is approved by hand. No bots, no spam, just people who care about evidence and honest debate.

Membership is free!

Create your free account

Back
Top Bottom