• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

marekdedic / rollup-plugin-htaccess / 9987592088

18 Jul 2024 07:47AM UTC coverage: 88.927% (-9.9%) from 98.805%
9987592088

Pull #50

github

marekdedic
Unified filesystem access functions
Pull Request #50: Added the ability to extract CSP from HTML <meta> tags

223 of 233 branches covered (95.71%)

14 of 44 new or added lines in 4 files covered. (31.82%)

257 of 289 relevant lines covered (88.93%)

281.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

10.34
/src/extractMetaCSP.ts
1
import { findAll } from "domutils";
2
import { ElementType, parseDocument } from "htmlparser2";
3
import { join } from "path";
4
import type { OutputOptions, PluginHooks } from "rollup";
5

6
import type { Options } from "./index";
7
import { escapeValue, readFile, writeFile } from "./utils";
8

9
/**
10
 * @public
11
 */
12
export interface ExtractMetaCSPEnabledOptions {
13
  enabled: true;
14
  htaccessFile?: string;
15
  files: Array<string>;
16
}
17

18
/**
19
 * @public
20
 */
21
export type ExtractMetaCSPOptions =
22
  | ExtractMetaCSPEnabledOptions
23
  | { enabled: false };
24

25
let outputOptions: OutputOptions = {};
12✔
26

27
function renderStart(outputOptionsValue: OutputOptions): void {
NEW
28
  outputOptions = outputOptionsValue;
×
29
}
30

31
async function extractCSPValuesFromHTMLFile(
32
  fileName: string,
33
): Promise<Array<string>> {
NEW
34
  let fileContents = "";
×
NEW
35
  try {
×
NEW
36
    fileContents = await readFile(fileName);
×
37
  } catch {
NEW
38
    return [];
×
39
  }
NEW
40
  const dom = parseDocument(fileContents, {
×
41
    withStartIndices: true,
42
    withEndIndices: true,
43
  });
NEW
44
  const cspMetaElems = findAll(
×
45
    (elem) =>
NEW
46
      elem.type === ElementType.Tag &&
×
47
      elem.name === "meta" &&
48
      elem.attribs["http-equiv"] === "content-security-policy",
49
    dom.children,
50
  );
NEW
51
  const cspValues = cspMetaElems.map((elem) => elem.attribs.content);
×
NEW
52
  for (const cspMetaElem of cspMetaElems) {
×
NEW
53
    fileContents =
×
54
      fileContents.substring(0, cspMetaElem.startIndex!) +
55
      fileContents.substring(cspMetaElem.endIndex! + 1);
56
  }
NEW
57
  await writeFile(fileName, fileContents);
×
NEW
58
  return cspValues;
×
59
}
60

61
async function writeCSPValuesToHtaccessFile(
62
  cspValues: Array<string>,
63
  options: ExtractMetaCSPEnabledOptions,
64
  htaccessFileName: string,
65
): Promise<void> {
66
  const path =
NEW
67
    options.htaccessFile ?? join(outputOptions.dir ?? "", htaccessFileName);
×
NEW
68
  let fileContents = "";
×
NEW
69
  try {
×
NEW
70
    fileContents = await readFile(path);
×
71
  } catch {
NEW
72
    throw new Error('Could not read htaccess file at path "' + path + '".');
×
73
  }
NEW
74
  fileContents +=
×
75
    cspValues
76
      .map(
77
        (value) =>
NEW
78
          'Header always set Content-Security-Policy "' +
×
79
          escapeValue(value) +
80
          '"',
81
      )
82
      .join("\n") + "\n";
NEW
83
  await writeFile(path, fileContents);
×
84
}
85

86
function closeBundle(
87
  options: ExtractMetaCSPEnabledOptions,
88
  htaccessFileName: string,
89
): PluginHooks["closeBundle"] {
NEW
90
  return {
×
91
    order: "post",
92
    sequential: true,
93
    handler: async (): Promise<void> => {
NEW
94
      const cspValues = (
×
95
        await Promise.all(
NEW
96
          options.files.map(async (file) => extractCSPValuesFromHTMLFile(file)),
×
97
        )
98
      ).flat();
NEW
99
      await writeCSPValuesToHtaccessFile(cspValues, options, htaccessFileName);
×
100
    },
101
  };
102
}
103

104
export function extractMetaCSP(options: Options): Partial<PluginHooks> {
105
  if (!options.extractMetaCSP.enabled) {
1,566✔
106
    return {};
1,566✔
107
  }
NEW
108
  return {
×
109
    renderStart,
110
    closeBundle: closeBundle(options.extractMetaCSP, options.fileName),
111
  };
112
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc