summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Bugzilla/Extension.pm11
-rw-r--r--Bugzilla/Install/Filesystem.pm301
-rwxr-xr-xextensions/create.pl3
-rwxr-xr-xshowdependencygraph.cgi24
-rw-r--r--template/en/default/extensions/web-readme.txt.tmpl29
5 files changed, 233 insertions, 135 deletions
diff --git a/Bugzilla/Extension.pm b/Bugzilla/Extension.pm
index 17b889b98..c7688d90b 100644
--- a/Bugzilla/Extension.pm
+++ b/Bugzilla/Extension.pm
@@ -649,6 +649,17 @@ case your templates should probably be in a directory like
F<extensions/Foo/template/en/default/page/foo/> so as not to conflict with
other pages that other extensions might add.
+=head2 CSS, JavaScript, and Images
+
+If you include CSS, JavaScript, and images in your extension that are
+served directly to the user (that is, they're not read by a script and
+then printed--they're just linked directly in your HTML), they should go
+into the F<web/> subdirectory of your extension.
+
+So, for example, if you had a CSS file called F<style.css> and your
+extension was called F<Foo>, your file would go into
+F<extensions/Foo/web/style.css>.
+
=head2 Disabling Your Extension
If you want your extension to be totally ignored by Bugzilla (it will
diff --git a/Bugzilla/Install/Filesystem.pm b/Bugzilla/Install/Filesystem.pm
index 9ee21ed35..0ca49a75c 100644
--- a/Bugzilla/Install/Filesystem.pm
+++ b/Bugzilla/Install/Filesystem.pm
@@ -54,6 +54,53 @@ use constant HT_DEFAULT_DENY => <<EOT;
deny from all
EOT
+###############
+# Permissions #
+###############
+
+# Used by the permissions "constants" below.
+sub _suexec { Bugzilla->localconfig->{'use_suexec'} };
+sub _group { Bugzilla->localconfig->{'webservergroup'} };
+
+# Writeable by the owner only.
+use constant OWNER_WRITE => 0600;
+# Executable by the owner only.
+use constant OWNER_EXECUTE => 0700;
+# A directory which is only writeable by the owner.
+use constant DIR_OWNER_WRITE => 0700;
+
+# A cgi script that the webserver can execute.
+sub WS_EXECUTE { _group() ? 0750 : 0755 };
+# A file that is read by cgi scripts, but is not ever read
+# directly by the webserver.
+sub CGI_READ { _group() ? 0640 : 0644 };
+# A file that is written to by cgi scripts, but is not ever
+# read or written directly by the webserver.
+sub CGI_WRITE { _group() ? 0660 : 0666 };
+# A file that is served directly by the web server.
+sub WS_SERVE { (_group() and !_suexec()) ? 0640 : 0644 };
+
+# A directory whose contents can be read or served by the
+# webserver (so even directories containing cgi scripts
+# would have this permission).
+sub DIR_WS_SERVE { (_group() and !_suexec()) ? 0750 : 0755 };
+# A directory that is read by cgi scripts, but is never accessed
+# directly by the webserver
+sub DIR_CGI_READ { _group() ? 0750 : 0755 };
+# A directory that is written to by cgi scripts, but where the
+# scripts never needs to overwrite files created by other
+# users.
+sub DIR_CGI_WRITE { _group() ? 0770 : 01777 };
+# A directory that is written to by cgi scripts, where the
+# scripts need to overwrite files created by other users.
+sub DIR_CGI_OVERWRITE { _group() ? 0770 : 0777 };
+
+# This can be combined (using "|") with other permissions for
+# directories that, in addition to their normal permissions (such
+# as DIR_CGI_WRITE) also have content served directly from them
+# (or their subdirectories) to the user, via the webserver.
+sub DIR_ALSO_WS_SERVE { _suexec() ? 0001 : 0 };
+
# This looks like a constant because it effectively is, but
# it has to call other subroutines and read the current filesystem,
# so it's defined as a sub. This is not exported, so it doesn't have
@@ -82,37 +129,6 @@ sub FILESYSTEM {
$localconfig =~ s/\.\Q$ENV{PROJECT}\E$//;
}
- my $ws_group = Bugzilla->localconfig->{'webservergroup'};
- my $use_suexec = Bugzilla->localconfig->{'use_suexec'};
-
- # The set of permissions that we use:
-
- # FILES
- # Executable by the web server
- my $ws_executable = $ws_group ? 0750 : 0755;
- # Executable by the owner only.
- my $owner_executable = 0700;
- # Readable by the web server.
- my $ws_readable = ($ws_group && !$use_suexec) ? 0640 : 0644;
- # Readable by the owner only.
- my $owner_readable = 0600;
- # Writeable by the web server.
- my $ws_writeable = $ws_group ? 0660 : 0666;
-
- # Script-readable files that should not be world-readable under suexec.
- my $script_readable = $use_suexec ? 0640 : $ws_readable;
-
- # DIRECTORIES
- # Readable by the web server.
- my $ws_dir_readable = ($ws_group && !$use_suexec) ? 0750 : 0755;
- # Readable only by the owner.
- my $owner_dir_readable = 0700;
- # Writeable by the web server.
- my $ws_dir_writeable = $ws_group ? 0770 : 01777;
- # The web server can overwrite files owned by other users,
- # in this directory.
- my $ws_dir_full_control = $ws_group ? 0770 : 0777;
-
# Note: When being processed by checksetup, these have their permissions
# set in this order: %all_dirs, %recurse_dirs, %all_files.
#
@@ -123,45 +139,48 @@ sub FILESYSTEM {
# --- FILE PERMISSIONS (Non-created files) --- #
my %files = (
- '*' => { perms => $ws_readable },
- '*.cgi' => { perms => $ws_executable },
- 'whineatnews.pl' => { perms => $ws_executable },
- 'collectstats.pl' => { perms => $ws_executable },
- 'checksetup.pl' => { perms => $owner_executable },
- 'importxml.pl' => { perms => $ws_executable },
- 'runtests.pl' => { perms => $owner_executable },
- 'testserver.pl' => { perms => $ws_executable },
- 'whine.pl' => { perms => $ws_executable },
- 'customfield.pl' => { perms => $owner_executable },
- 'email_in.pl' => { perms => $ws_executable },
- 'sanitycheck.pl' => { perms => $ws_executable },
- 'jobqueue.pl' => { perms => $owner_executable },
- 'migrate.pl' => { perms => $owner_executable },
- 'install-module.pl' => { perms => $owner_executable },
-
- # Set the permissions for localconfig the same across all
- # PROJECTs.
- $localconfig => { perms => $script_readable },
- "$localconfig.*" => { perms => $script_readable },
- "$localconfig.old" => { perms => $owner_readable },
-
- 'contrib/README' => { perms => $owner_readable },
- 'contrib/*/README' => { perms => $owner_readable },
- 'docs/makedocs.pl' => { perms => $owner_executable },
- 'docs/style.css' => { perms => $ws_readable },
- 'docs/*/rel_notes.txt' => { perms => $ws_readable },
- 'docs/*/README.docs' => { perms => $owner_readable },
- "$datadir/params" => { perms => $ws_writeable },
- "$datadir/old-params.txt" => { perms => $owner_readable },
- "$extensionsdir/create.pl" => { perms => $owner_executable },
+ '*' => { perms => OWNER_WRITE },
+ # Some .pl files are WS_EXECUTE because we want
+ # users to be able to cron them or otherwise run
+ # them as a secure user, like the webserver owner.
+ '*.cgi' => { perms => WS_EXECUTE },
+ 'whineatnews.pl' => { perms => WS_EXECUTE },
+ 'collectstats.pl' => { perms => WS_EXECUTE },
+ 'importxml.pl' => { perms => WS_EXECUTE },
+ 'testserver.pl' => { perms => WS_EXECUTE },
+ 'whine.pl' => { perms => WS_EXECUTE },
+ 'email_in.pl' => { perms => WS_EXECUTE },
+ 'sanitycheck.pl' => { perms => WS_EXECUTE },
+ 'checksetup.pl' => { perms => OWNER_EXECUTE },
+ 'runtests.pl' => { perms => OWNER_EXECUTE },
+ 'jobqueue.pl' => { perms => OWNER_EXECUTE },
+ 'migrate.pl' => { perms => OWNER_EXECUTE },
+ 'install-module.pl' => { perms => OWNER_EXECUTE },
+
+ 'Bugzilla.pm' => { perms => CGI_READ },
+ "$localconfig*" => { perms => CGI_READ },
+ 'bugzilla.dtd' => { perms => WS_SERVE },
+ 'mod_perl.pl' => { perms => WS_SERVE },
+ 'robots.txt' => { perms => WS_SERVE },
+
+ 'contrib/README' => { perms => OWNER_WRITE },
+ 'contrib/*/README' => { perms => OWNER_WRITE },
+ 'docs/bugzilla.ent' => { perms => OWNER_WRITE },
+ 'docs/makedocs.pl' => { perms => OWNER_EXECUTE },
+ 'docs/style.css' => { perms => WS_SERVE },
+ 'docs/*/rel_notes.txt' => { perms => WS_SERVE },
+ 'docs/*/README.docs' => { perms => OWNER_WRITE },
+ "$datadir/params" => { perms => CGI_WRITE },
+ "$datadir/old-params.txt" => { perms => OWNER_WRITE },
+ "$extensionsdir/create.pl" => { perms => OWNER_EXECUTE },
);
# Directories that we want to set the perms on, but not
# recurse through. These are directories we didn't create
# in checkesetup.pl.
my %non_recurse_dirs = (
- '.' => $ws_dir_readable,
- docs => $ws_dir_readable,
+ '.' => DIR_WS_SERVE,
+ docs => DIR_WS_SERVE,
);
# This sets the permissions for each item inside each of these
@@ -170,50 +189,64 @@ sub FILESYSTEM {
# the webserver.
my %recurse_dirs = (
# Writeable directories
- "$datadir/template" => { files => $ws_readable,
- dirs => $ws_dir_full_control },
- $attachdir => { files => $ws_writeable,
- dirs => $ws_dir_writeable },
- $webdotdir => { files => $ws_writeable,
- dirs => $ws_dir_writeable },
- graphs => { files => $ws_writeable,
- dirs => $ws_dir_writeable },
+ "$datadir/template" => { files => CGI_READ,
+ dirs => DIR_CGI_OVERWRITE },
+ $attachdir => { files => CGI_WRITE,
+ dirs => DIR_CGI_WRITE },
+ $webdotdir => { files => WS_SERVE,
+ dirs => DIR_CGI_WRITE | DIR_ALSO_WS_SERVE },
+ graphs => { files => WS_SERVE,
+ dirs => DIR_CGI_WRITE | DIR_ALSO_WS_SERVE },
# Readable directories
- "$datadir/mining" => { files => $ws_readable,
- dirs => $ws_dir_readable },
- "$libdir/Bugzilla" => { files => $ws_readable,
- dirs => $ws_dir_readable },
- $extlib => { files => $ws_readable,
- dirs => $ws_dir_readable },
- $templatedir => { files => $ws_readable,
- dirs => $ws_dir_readable },
- $extensionsdir => { files => $ws_readable,
- dirs => $ws_dir_readable },
- images => { files => $ws_readable,
- dirs => $ws_dir_readable },
- css => { files => $ws_readable,
- dirs => $ws_dir_readable },
- js => { files => $ws_readable,
- dirs => $ws_dir_readable },
- $skinsdir => { files => $ws_readable,
- dirs => $ws_dir_readable },
- t => { files => $owner_readable,
- dirs => $owner_dir_readable },
- 'docs/*/html' => { files => $ws_readable,
- dirs => $ws_dir_readable },
- 'docs/*/pdf' => { files => $ws_readable,
- dirs => $ws_dir_readable },
- 'docs/*/txt' => { files => $ws_readable,
- dirs => $ws_dir_readable },
- 'docs/*/images' => { files => $ws_readable,
- dirs => $ws_dir_readable },
- 'docs/lib' => { files => $owner_readable,
- dirs => $owner_dir_readable },
- 'docs/*/xml' => { files => $owner_readable,
- dirs => $owner_dir_readable },
- 'contrib' => { files => $owner_executable,
- dirs => $owner_dir_readable, },
+ "$datadir/mining" => { files => CGI_READ,
+ dirs => DIR_CGI_READ },
+ "$libdir/Bugzilla" => { files => CGI_READ,
+ dirs => DIR_CGI_READ },
+ $extlib => { files => CGI_READ,
+ dirs => DIR_CGI_READ },
+ $templatedir => { files => CGI_READ,
+ dirs => DIR_CGI_READ },
+ # Directories in the extensions/ dir are WS_SERVE so that
+ # the web/ directories can be served by the web server.
+ # But, for extra security, we deny direct webserver access to
+ # the lib/ and template/ directories of extensions.
+ $extensionsdir => { files => CGI_READ,
+ dirs => DIR_WS_SERVE },
+ "$extensionsdir/*/lib" => { files => CGI_READ,
+ dirs => DIR_CGI_READ },
+ "$extensionsdir/*/template" => { files => CGI_READ,
+ dirs => DIR_CGI_READ },
+
+ # Content served directly by the webserver
+ images => { files => WS_SERVE,
+ dirs => DIR_WS_SERVE },
+ js => { files => WS_SERVE,
+ dirs => DIR_WS_SERVE },
+ $skinsdir => { files => WS_SERVE,
+ dirs => DIR_WS_SERVE },
+ 'docs/*/html' => { files => WS_SERVE,
+ dirs => DIR_WS_SERVE },
+ 'docs/*/pdf' => { files => WS_SERVE,
+ dirs => DIR_WS_SERVE },
+ 'docs/*/txt' => { files => WS_SERVE,
+ dirs => DIR_WS_SERVE },
+ 'docs/*/images' => { files => WS_SERVE,
+ dirs => DIR_WS_SERVE },
+ "$extensionsdir/*/web" => { files => WS_SERVE,
+ dirs => DIR_WS_SERVE },
+
+ # Directories only for the owner, not for the webserver.
+ '.bzr' => { files => OWNER_WRITE,
+ dirs => DIR_OWNER_WRITE },
+ t => { files => OWNER_WRITE,
+ dirs => DIR_OWNER_WRITE },
+ 'docs/lib' => { files => OWNER_WRITE,
+ dirs => DIR_OWNER_WRITE },
+ 'docs/*/xml' => { files => OWNER_WRITE,
+ dirs => DIR_OWNER_WRITE },
+ 'contrib' => { files => OWNER_EXECUTE,
+ dirs => DIR_OWNER_WRITE, },
);
# --- FILES TO CREATE --- #
@@ -221,27 +254,31 @@ sub FILESYSTEM {
# The name of each directory that we should actually *create*,
# pointing at its default permissions.
my %create_dirs = (
- $datadir => $ws_dir_full_control,
- "$datadir/mining" => $ws_dir_readable,
- "$datadir/extensions" => $ws_dir_readable,
- $attachdir => $ws_dir_writeable,
- $extensionsdir => $ws_dir_readable,
- graphs => $ws_dir_writeable,
- $webdotdir => $ws_dir_writeable,
- "$skinsdir/custom" => $ws_dir_readable,
- "$skinsdir/contrib" => $ws_dir_readable,
+ # This is DIR_ALSO_WS_SERVE because it contains $webdotdir.
+ $datadir => DIR_CGI_OVERWRITE | DIR_ALSO_WS_SERVE,
+ # Directories that are read-only for cgi scripts
+ "$datadir/mining" => DIR_CGI_READ,
+ "$datadir/extensions" => DIR_CGI_READ,
+ $extensionsdir => DIR_CGI_READ,
+ # Directories that cgi scripts can write to.
+ $attachdir => DIR_CGI_WRITE,
+ graphs => DIR_CGI_WRITE | DIR_ALSO_WS_SERVE,
+ $webdotdir => DIR_CGI_WRITE | DIR_ALSO_WS_SERVE,
+ # Directories that contain content served directly by the web server.
+ "$skinsdir/custom" => DIR_WS_SERVE,
+ "$skinsdir/contrib" => DIR_WS_SERVE,
);
# The name of each file, pointing at its default permissions and
# default contents.
my %create_files = (
- "$datadir/extensions/additional" => { perms => $ws_readable,
+ "$datadir/extensions/additional" => { perms => CGI_READ,
contents => '' },
# We create this file so that it always has the right owner
# and permissions. Otherwise, the webserver creates it as
# owned by itself, which can cause problems if jobqueue.pl
# or something else is not running as the webserver or root.
- "$datadir/mailer.testfile" => { perms => $ws_writeable,
+ "$datadir/mailer.testfile" => { perms => CGI_WRITE,
contents => '' },
);
@@ -251,18 +288,18 @@ sub FILESYSTEM {
foreach my $skin_dir ("$skinsdir/custom", <$skinsdir/contrib/*>) {
next if basename($skin_dir) =~ /^cvs$/i;
foreach my $base_css (<$skinsdir/standard/*.css>) {
- _add_custom_css($skin_dir, basename($base_css), \%create_files, $ws_readable);
+ _add_custom_css($skin_dir, basename($base_css), \%create_files);
}
foreach my $dir_css (<$skinsdir/standard/*/*.css>) {
$dir_css =~ s{.+?([^/]+/[^/]+)$}{$1};
- _add_custom_css($skin_dir, $dir_css, \%create_files, $ws_readable);
+ _add_custom_css($skin_dir, $dir_css, \%create_files);
}
}
# Because checksetup controls the creation of index.html separately
# from all other files, it gets its very own hash.
my %index_html = (
- 'index.html' => { perms => $ws_readable, contents => <<EOT
+ 'index.html' => { perms => WS_SERVE, contents => <<EOT
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
@@ -279,23 +316,27 @@ EOT
# Because checksetup controls the .htaccess creation separately
# by a localconfig variable, these go in a separate variable from
# %create_files.
+ #
+ # Note that these get WS_SERVE as their permission
+ # because they're *read* by the webserver, even though they're not
+ # actually, themselves, served.
my %htaccess = (
- "$attachdir/.htaccess" => { perms => $ws_readable,
+ "$attachdir/.htaccess" => { perms => WS_SERVE,
contents => HT_DEFAULT_DENY },
- "$libdir/Bugzilla/.htaccess" => { perms => $ws_readable,
+ "$libdir/Bugzilla/.htaccess" => { perms => WS_SERVE,
contents => HT_DEFAULT_DENY },
- "$extlib/.htaccess" => { perms => $ws_readable,
+ "$extlib/.htaccess" => { perms => WS_SERVE,
contents => HT_DEFAULT_DENY },
- "$templatedir/.htaccess" => { perms => $ws_readable,
+ "$templatedir/.htaccess" => { perms => WS_SERVE,
contents => HT_DEFAULT_DENY },
- 'contrib/.htaccess' => { perms => $ws_readable,
+ 'contrib/.htaccess' => { perms => WS_SERVE,
contents => HT_DEFAULT_DENY },
- 't/.htaccess' => { perms => $ws_readable,
+ 't/.htaccess' => { perms => WS_SERVE,
contents => HT_DEFAULT_DENY },
- "$datadir/.htaccess" => { perms => $ws_readable,
+ "$datadir/.htaccess" => { perms => WS_SERVE,
contents => HT_DEFAULT_DENY },
- "$webdotdir/.htaccess" => { perms => $ws_readable, contents => <<EOT
+ "$webdotdir/.htaccess" => { perms => WS_SERVE, contents => <<EOT
# Restrict access to .dot files to the public webdot server at research.att.com
# if research.att.com ever changes their IP, or if you use a different
# webdot server, you'll need to edit this
@@ -414,8 +455,8 @@ EOT
# A simple helper for creating "empty" CSS files.
sub _add_custom_css {
- my ($skin_dir, $path, $create_files, $perms) = @_;
- $create_files->{"$skin_dir/$path"} = { perms => $perms, contents => <<EOT
+ my ($skin_dir, $path, $create_files) = @_;
+ $create_files->{"$skin_dir/$path"} = { perms => WS_SERVE, contents => <<EOT
/*
* Custom rules for $path.
* The rules you put here override rules in that stylesheet.
diff --git a/extensions/create.pl b/extensions/create.pl
index c4d911c2a..1b54e6b33 100755
--- a/extensions/create.pl
+++ b/extensions/create.pl
@@ -41,7 +41,7 @@ mkpath($extension_dir)
|| die "$extension_dir already exists or cannot be created.\n";
my $lcname = lc($name);
-foreach my $path (qw(lib template/en/default/hook),
+foreach my $path (qw(lib web template/en/default/hook),
"template/en/default/$lcname")
{
mkpath("$extension_dir/$path") || die "$extension_dir/$path: $!";
@@ -55,6 +55,7 @@ my %create_files = (
'config.pm.tmpl' => 'Config.pm',
'extension.pm.tmpl' => 'Extension.pm',
'util.pm.tmpl' => 'lib/Util.pm',
+ 'web-readme.txt.tmpl' => 'web/README',
'hook-readme.txt.tmpl' => 'template/en/default/hook/README',
'name-readme.txt.tmpl' => "template/en/default/$lcname/README",
);
diff --git a/showdependencygraph.cgi b/showdependencygraph.cgi
index a036ee0c9..5fadce998 100755
--- a/showdependencygraph.cgi
+++ b/showdependencygraph.cgi
@@ -29,6 +29,7 @@ use File::Temp;
use Bugzilla;
use Bugzilla::Constants;
+use Bugzilla::Install::Filesystem;
use Bugzilla::Util;
use Bugzilla::Error;
use Bugzilla::Bug;
@@ -114,7 +115,13 @@ if (!defined $cgi->param('id') && $display ne 'doall') {
my ($fh, $filename) = File::Temp::tempfile("XXXXXXXXXX",
SUFFIX => '.dot',
- DIR => $webdotdir);
+ DIR => $webdotdir,
+ UNLINK => 1);
+
+chmod Bugzilla::Install::Filesystem::CGI_WRITE, $filename
+ or warn install_string('chmod_failed', { path => $filename,
+ error => $! });
+
my $urlbase = Bugzilla->params->{'urlbase'};
print $fh "digraph G {";
@@ -244,8 +251,6 @@ foreach my $k (keys(%seen)) {
print $fh "}\n";
close $fh;
-chmod 0777, $filename;
-
my $webdotbase = Bugzilla->params->{'webdotbase'};
if ($webdotbase =~ /^https?:/) {
@@ -263,13 +268,18 @@ if ($webdotbase =~ /^https?:/) {
my ($pngfh, $pngfilename) = File::Temp::tempfile("XXXXXXXXXX",
SUFFIX => '.png',
DIR => $webdotdir);
+
+ chmod Bugzilla::Install::Filesystem::WS_SERVE, $pngfilename
+ or warn install_string('chmod_failed', { path => $pngfilename,
+ error => $! });
+
binmode $pngfh;
open(DOT, "\"$webdotbase\" -Tpng $filename|");
binmode DOT;
print $pngfh $_ while <DOT>;
close DOT;
close $pngfh;
-
+
# On Windows $pngfilename will contain \ instead of /
$pngfilename =~ s|\\|/|g if ON_WINDOWS;
@@ -287,12 +297,18 @@ if ($webdotbase =~ /^https?:/) {
my ($mapfh, $mapfilename) = File::Temp::tempfile("XXXXXXXXXX",
SUFFIX => '.map',
DIR => $webdotdir);
+
+ chmod Bugzilla::Install::Filesystem::WS_SERVE, $mapfilename
+ or warn install_string('chmod_failed', { path => $mapfilename,
+ error => $! });
+
binmode $mapfh;
open(DOT, "\"$webdotbase\" -Tismap $filename|");
binmode DOT;
print $mapfh $_ while <DOT>;
close DOT;
close $mapfh;
+
$vars->{'image_map'} = CreateImagemap($mapfilename);
}
diff --git a/template/en/default/extensions/web-readme.txt.tmpl b/template/en/default/extensions/web-readme.txt.tmpl
new file mode 100644
index 000000000..55e593914
--- /dev/null
+++ b/template/en/default/extensions/web-readme.txt.tmpl
@@ -0,0 +1,29 @@
+[%# The contents of this file are subject to the Mozilla Public
+ # License Version 1.1 (the "License"); you may not use this file
+ # except in compliance with the License. You may obtain a copy of
+ # the License at http://www.mozilla.org/MPL/
+ #
+ # Software distributed under the License is distributed on an "AS
+ # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ # implied. See the License for the specific language governing
+ # rights and limitations under the License.
+ #
+ # The Original Code is the Bugzilla Bug Tracking System.
+ #
+ # The Initial Developer of the Original Code is Everything Solved, Inc.
+ # Portions created by the Initial Developer are Copyright (C) 2010 the
+ # Initial Developer. All Rights Reserved.
+ #
+ # Contributor(s):
+ # Max Kanat-Alexander <mkanat@bugzilla.org>
+ #%]
+
+[% PROCESS global/variables.none.tmpl %]
+
+Web-accessible files, like JavaScript, CSS, and images go in this
+directory. You can reference them directly in your HTML. For example,
+if you have a file called "style.css" and your extension is called
+"Foo", you would put it in "extensions/Foo/web/style.css", and then
+you could link to it in HTML like:
+
+<link href="extensions/Foo/web/style.css" rel="stylesheet" type="text/css">